首页 > 其他分享 >记录--JS 的垃圾回收机制

记录--JS 的垃圾回收机制

时间:2023-08-16 18:23:00浏览次数:33  
标签:obj -- 引用 回收 JS 对象 内存 垃圾

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

垃圾回收(Garbage Collection)是一种内存管理机制,用于检测和清理不再被程序使用的内存,这些不再被使用的内存就被称为垃圾。垃圾回收器会在 JS 引擎(浏览器或者 nodejs)内部周期性地运行,一般情况下无需开发者手动操作。

但是,了解垃圾回收机制的工作原理有助于我们写出更加高效的 JS 代码,使 JS 引擎更好的帮助我们完成垃圾回收,避免我们开发的应用出现内存泄漏问题。

垃圾是怎样产生的?

JS 中的数据类型有原始类型和引用类型,原始类型占用的内存极小,一般是字符串、数字、布尔值这些,他们被存放在栈(stack)中。引用类型可以是数组、普通对象或者函数,他们一般会包含较多的数据,所以引用类型的实际数据存放在内存的堆(heap)中,然后在栈中会存放一个指向该实际数据的地址。

const str = "abc" // 原始类型
const obj = { foo: "bar" } // 引用类型

在 JS 中每声明一个变量,该应用占用的内存就会相应的增加,但机器的内存是有限的,内存的占用不能无限制的增加。所以 JS 引擎需要适时的回收内存,释放内存,保留性能,从而运行使程序流畅运行。

垃圾回收机制会回收哪些垃圾?

当一个对象不在任何引用被引用时,它就变得不可达,垃圾回收器就需要将这些不可达对象标记为可回收,并在适当的时机回收它们的内存。

以下面这段代码为例:

function myFunction() {
  const obj = { foo: "bar" }
  // do something
}

myFunction()

这段代码中的 obj 对象在 myFunction 函数执行之后,就已经没有被引用了,就需要被回收。

但当某个对象从开发角度上来说不再被使用了,却意外的仍然在某个地方被引用,垃圾回收器就无法回收它的内存,就会造成内存泄漏(内存逐渐累积,程序占用的内存越来越多,当超过系统的可用内存时,就会造成程序崩溃)。

比如下面这段代码中 obj 对象就可能不会被回收:

function myFunction() {
  const obj = { foo: "bar" }

  setTimeout(() => {
    console.log(obj.foo)
  }, 1000)
}

myFunction()

因为 obj 作为闭包中的引用传递给了定时器的回调函数,即使 myFunction 执行完毕,由于定时器没有被清除,obj 仍然被定时器回调函数持有引用,就可能导致 obj 不会被垃圾回收。

垃圾回收的算法

JavaScript 中的垃圾回收机制主要基于以下两个原则:

引用计数(Reference Counting)

每个对象都有一个引用计数,用于记录有多少个引用指向该对象。当引用计数变为零时,表示没有任何引用指向该对象,因此该对象可以被回收。

标记-清除(Mark and Sweep)

这是一种更常见的垃圾回收算法。它从根对象(如全局对象、当前执行上下文中的变量等)开始,通过遍历对象之间的引用关系,标记所有可达的对象。然后,回收器会清除未标记的对象,即不可达的对象,释放其内存。

Chrome 和 nodejs 的垃圾回收算法

Chrome 和 nodejs 都采用了谷_歌开源的 V8 引擎。V8 引擎的垃圾回收机制采用了标记清除算法,但在此基础又做了一些优化。

V8 引擎将内存分为新生代(Young Generation)和老生代(Old Generation)。大多数对象在新生代中创建,经过一定时间后,如果它们仍然存活,就会被晋升到老生代。

Scavenger 垃圾回收(新生代)

新生代使用了 Scavenger 垃圾回收算法,它将内存划分为一个存活区域和一个空闲区域。对象首先被分配到存活区域,当存活区域满时,会执行垃圾回收操作,将存活的对象复制到空闲区域,并清空存活区域。

Mark-Sweep-Compact 垃圾回收(老生代)

老生代中使用了 Mark-Sweep-Compact(标记-清除-整理)垃圾回收算法。它首先标记所有的存活对象,然后清除掉未被标记的对象,最后进行内存整理,使存活对象连续排列,减少内存碎片。

增量垃圾回收

V8 引擎还支持增量垃圾回收。他会将垃圾回收操作分成多个小步骤执行,每个步骤之间会插入一些 JavaScript 代码的执行,从而避免长时间的垃圾回收造成的界面卡顿。

空闲时间垃圾回收

V8 引擎还在空闲时间执行部分垃圾回收操作,以充分利用闲置的计算资源。这些时间段可能是在程序等待用户输入、网络请求返回、或者其他暂时没有任务需要处理的情况下出现的。

需要手动清除的内存

垃圾回收机制会根据算法智能的回收大部分的内存,但由于业务逻辑的关系,它无法明确知道在我们的写的(垃圾)代码中,哪些对象其实是不再使用的,所以我们在开发过程中需要及时的清除不需要的事件监听、定时器、计时器,避免循环引用,以及避免使用闭包。

清除事件监听

const myButton = document.getElementById("myButton")

function handleClick() {
  console.log("Button clicked!")
}

// 添加事件监听器
myButton.addEventListener("click", handleClick)

// 在页面卸载或元素移除时解除事件监听器
window.addEventListener("beforeunload", () => {
  myButton.removeEventListener("click", handleClick)
})

执清除定时器、计时器

const timer = setTimeout(() => {}, 500)

// 在页面卸载或元素移除时解除事件监听器
window.addEventListener("beforeunload", () => {
  clearTimeout(timer)
})

手动调用垃圾回收

一般情况下我们无需手动调用垃圾回收,但有些浏览器支持主动触发垃圾回收。

IE 浏览器

if (typeof window.CollectGarbage === "function") {
  window.CollectGarbage()
}

Opera 浏览器

if (window.opera && typeof window.opera.collect === "function") {
  window.opera.collect()
}

本文转载于:

https://juejin.cn/post/7267434484505788468

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:obj,--,引用,回收,JS,对象,内存,垃圾
From: https://www.cnblogs.com/smileZAZ/p/17635894.html

相关文章

  • Prometheus+Grafana 监控服务器资源
    一、Prometheus1.安装Prometheusdockerpullprom/prometheusdockerrun-itd--name=prometheus--restart=always-p9090:9090prom/prometheus容器创建成功后,即可通过浏览器访问http://本地ip:9090来进行验证2.安装Grafanamkdir/data/grafana-storage#创建目录用于......
  • Winform控件自适应窗体大小
    思路[参考他人]:1>保存窗体的初始宽度和高度;2>保存窗体内所有控件的初始宽度,初始高度和坐标;3>窗体的Resize事件触发时,计算新的Size和初始Size的比例prec;4>遍历窗体内所有控件,将其的Size和坐标乘以prec;代码:窗体注册,保存窗体及其所有控件的初始尺寸,编写Resize事件逻辑:public......
  • WebSocket_入门案例
           ......
  • 业务安全情报第20期 | 一个阅读活动,却吸引来大批羊毛党
    某新媒体小程序推出了一项有奖阅读活动,吸引了大量用户参与。该活动允许用户通过阅读推文任务获得红包奖励,并可直接提现。不过,这一活动很快遭遇了黑灰产问题,导致用户福利无法实现。 背景看新闻,领红包并不是新鲜事,2018年9月15日,号称一款新生代内容资讯APP的去趣头条,凭借着“......
  • UE 5 NavMesh 烘培 逻辑流程
     关于UE引擎层面的东西:在向场景重拖入一个NavMeshBoundsVolume时(或者修改时).会调用voidUNavigationSystemV1::PerformNavigationBoundsUpdate(constTArray&UpdateRequests)然后会创建/更新一个NavigationDataActor对象到场景中,名字默认是RecastNavMesh-Default......
  • 【转载】c++调用win32API控制打印机打印
    原文:https://blog.csdn.net/cheng448208985/article/details/55510687win32实现将原始数据发送给打印机1、调用OpenPrinter()打开打印机,获取打印机句柄。2、初始化DOCINFO打印机结构体。3、调用StartDocPrinter()表明应用程序准备发送文档数据给打印机。4、调用StartPagePrin......
  • 微信开发之一键修改好友备注的技术实现
    简要描述:修改好友备注请求URL:http://域名地址/modifyRemark请求方式:POST请求头Headers:Content-Type:application/jsonAuthorization:login接口返回参数:参数名必选类型说明wId是string登录实例标识wcId是string好友微信idremark是string好友......
  • sql row_number(),rank(),row_number()的区别
    第一个,row_nubmer(),这个排序函数的特点是相同数据,先查出的排名在前,没有重复值。像我们这里呢sal相同,先查出来的数据的rank排名优先。如下图:partitionby相当于分组查询第二个,rank()函数,是跳跃排序,相同数据(这里为sal列相同)排名相同,比如并列第1,则两行数据(这里为rank列)......
  • SkyEye操作指南:连接TI CCS的IDE调试
    现代电力电子控制系统的开发中,DSP芯片以其优越的运算性能在控制算法领域得到越来越广泛的应用。传统的DSP开发过程往往需要在完成控制系统仿真与程序设计后,才能根据比对结果进行程序修改,全过程还需要硬件电路工程师的配合,开发效率低下,灵活性差。为了快速验证控制算法,使仿真与开发......
  • sql复习
    第一章第二件事情:数据库了解1.学习前提条件--会一门语言2.为什么学习数据库?所有的电子设备操作核心----数据编程语言如何进行数据处理:1)--控制台打印数据2)--变量3)---数组4)---Java---集合框架--临时......