JS垃圾回收机制

一、什么是垃圾回收

在说垃圾回收之前,我们首先需要了解的是,什么是垃圾?为什么要进行垃圾回收?

垃圾: JS中的函数,变量,对象等都需要占用一定的内存,当这些东西不再被使用的时候,就变成了垃圾

  • 已经调用完毕的函数作用域及其内部的值
  • 值为 null 值
  • 无法被访问到的值

上面已经说了,JS中的所有的变量都会占用内存,当这些变量变成垃圾的时候,如果不进行回收,内存就会被一直占用,随着程序的运行,垃圾也会越来越多,总有一刻,内存会被占满,程序也就无法运行了

垃圾回收: 程序在运行过程中会产生很多垃圾,这些垃圾会占用很多的内存,JS引擎会定期对这些垃圾进行清除

二、垃圾回收机制

JavaScript 内置的引擎中有一个叫做垃圾回收器的程序,这个程序每隔一段时间就会执行一次

JavaScript 中有一个很重要的概念叫做可达性,即以任意一种方式,只要能被访问到的值,就继续让它保存在内存中,否则,说明这个值已经不被引用,需要被清理

基于这个思路,有以下两种常见的回收策略

1. 引用计数

跟踪记录每个值被引用的次数

  • 当一个变量被声明,并将一个引用类型值赋值给它时,这个引用类型值的引用次数就为 1
  • 如果这个值又被赋值给了另一个对象,则这个值的引用次数就 +1
  • 如果被赋值的变量又有了新的值,也就是说这个值被其它值覆盖了,则引用次数 -1

当这个值的引用次数变为 0 的时候,说明这个值已经无法被访问,这个值所占的内存也就需要被回收,当下一次垃圾回收器运行时,就会清理掉这个值

听起来这是一个很不错的策略,但是这个方法有一个很严重的缺陷,就是循环引用时引用值永远不为 0

let objA = new Object();
let objB = new Object();

objA.a = objB;
objB.b = objA;

在上面的案例中,objAobjB 相互调用,它们的引用计数值都为 2,因此,这两个值也就永远无法被垃圾回收机制清除

2. 标记清除

这是目前大部分浏览器中的 JavaScript 引擎 都在采用的方法

垃圾回收机制运行时会将所有的变量都加上一个标记,假设为 × ,表示待删除,然后遍历一遍所有的变量,将所有能够访问到值的变量标记去掉,剩下那些含有 × 标记的变量全部清除,回收其所占用的内存

标记清除法当然也有其局限性

  • 比如每次运行垃圾回收器都要遍历一次所有的变量,这是一件比较消耗性能的事,而且垃圾回收器运行的频率又该如何去定,频率太高影响性能,频率太低垃圾无法及时回收
  • JS是单线程语言,垃圾回收器运行时,会阻塞其它程序的运行,可能会造成页面卡顿(当项目很大,内存占用较多,清理需要较长时间时,有可能会感知到卡顿)
  • 清理完成后,原本垃圾所占用的内存是碎片化的,位置不会发生改变,虽然清理了内存,但新的对象如果想要存入这些内存中,就需要先遍历一遍这些碎片内存,确定哪些内存容量合适,再选择一个容量差不多的
    请添加图片描述

三、V8对垃圾回收的优化

我们已经知道,大部分浏览器使用的都是标记清除法,所以 V8 引擎 对垃圾回收的优化,针对的也是标记清除法

上面已经提到了,标记清除法有着一些局限性,V8针对这些局限性,进行一些优化,它的优化策略如下

将内存分为两部分,分别叫 新生代老生代,新生代又分为 正在使用的区域闲置区域,我们知道,垃圾回收器的运行是按照一定的频率反复执行的,这期间有一些变量可能经历了多次回收依然存在,这说明该变量可能在很长一段时间内都不会被回收,如果每次执行垃圾回收都要遍历它,尤其是当这个变量内容很多时,这是一件既耗时又没有意义的事

V8 引擎将那些经历多次垃圾回收依然存在的变量放入到 老生代 中保存,以及那些占用内存比较大的变量变量也放入老生代中,其余变量放入新生代中,老生代执行垃圾回收的频率较低,新生代较高,每次对新生代执行垃圾回收时,对正在使用的区域进行标记,标记完成后复制到闲置区域中,在闲置区域中进行清除并对内存进行整理排序,完成后再把闲置区域转换成使用区域,原本的使用区域转成闲置区域,并将其中的所有值全部清除

  • 经过多次垃圾回收依然存在的对象放到老生代内存中
  • 单个对象占用超过新生代内存25%的放入老生代内存中
  • 新生代的内存容量一般在 1 - 8M区间
  • 在使用区域进行标记,在闲置区域进行回收和整理,完成后互换身份,并将新的闲置区域内存清空(因为清理是在闲置内存中进行,避免了阻塞)
  • 除了定期运行垃圾回收器,当新生代内存即将占满时,也会立即触发垃圾回收器
    请添加图片描述
    其实上面这样描述是不太准确的,JS 中的原始值会存在栈内存中,引用值存在堆内存中,将引用值赋值给变量时,并不是把这个引用对象重新复制一份,而且创建一个指向它在堆内存中位置的指针,将这个指针存入栈内存中,垃圾回收主要针对的是堆内存,虽然这样描述更准确,但却不容易理解,所以如果不能理解这段话,仅看上面那段即可
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温其如玉_zxh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值