JavaScript中的垃圾回收机制
2023-12-07 15:08:07 0 举报
AI智能生成
JavaScript中的v8引擎是如何进行垃圾回收的
作者其他创作
大纲/内容
分支主题
手动回收
何时分配内存、何时销毁内存都是由代码控制的
自动回收
产生的垃圾数据是由垃圾回收器来释放的
垃圾是如何产生的?
执行某个函数
创建函数的执行上下文
执行上下文压入调用栈
记录执行状态的指针(ESP)指向当前上下文,表示正在执行
ESP下移,销毁函数栈中的执行上下文
堆中的内存依旧被占用
使用 Js 的垃圾回收起进行回收堆中的垃圾数据
假说
代际假说
第一个是大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,很快就变得不可访问;
第二个是不死的对象,会活得更久。
分代假说
弱分代假说
绝大多数的对象存活时间很短,朝生夕灭。
强分代假说
熬过越多次的垃圾回收次数,对象越难被消灭。
跨代引用假说
跨代引用相对于同代引用而言仅仅只占一小部分。
v8的内存结构
新生代(new_space)
大多数的对象开始都会被分配在这里,这个区域相对较小但是垃圾回收特别频繁,该区域被分为两半,一半用来分配内存,另一半用于在垃圾回收时将需要保留的对象复制过来
老生代(old_space)
新生代中的对象在存活一段时间后就会被转移到老生代内存区,相对于新生代该内存区域的垃圾回收频率较低。老生代又分为老生代指针区和老生代数据区,前者包含大多数可能存在指向其他对象的指针的对象,后者只保存原始数据对象,这些对象没有指向其他对象的指针。
大对象区(large_object_space)
存放体积超越其他区域大小的对象,每个对象都会有自己的内存,垃圾回收不会移动大对象区。
代码区(code_space)
代码对象,会被分配在这里,唯一拥有执行权限的内存区域。
map 区(map_space)
存放Cell和Map,每个区域都是存放相同大小的元素,结构简单
概念
V8的垃圾回收策略主要是基于分代式垃圾回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法。
分代式垃圾回收
新生代
新生代中存放的是生存时间短的对象
容量小,通常 1~8M
副垃圾回收器负责回收
Scavenge 算法回收
并行回收策略
启动多个线程来负责新生代中的垃圾清理操作,这些线程同时将对象空间中的数据移动到空闲区域,这个过程中由于数据地址会发生改变,所以还需要同步更新引用这些对象的指针,此即并行回收
老生代
老生代中存放的生存时间久的对象
主垃圾回收器负责回收
标记-整理(Mark-Compact)算法回收
增量标记
垃圾回收的工作流程
1.标记对象
标记空间中活动对象和非活动对象。所谓活动对象就是还在使用的对象,非活动对象就是可以进行垃圾回收的对象。
增量标记
2.回收非活动对象所占据的内存
其实就是在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。
惰性清理
3.内存整理
一般来说,频繁回收对象后,内存中就会存在大量不连续空间,我们把这些不连续的内存空间称为内存碎片。当内存中出现了大量的内存碎片之后,如果需要分配较大连续内存的时候,就有可能出现内存不足的情况。所以最后一步需要整理这些内存碎片,但这步其实是可选的
垃圾回收算法
Scavenge 算法
所谓 Scavenge 算法,是把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域
对象区域
新加入的对象都会存放到对象区域
当对象区域快被写满时,就需要执行一次垃圾清理操作。
空闲区域
流程
对象区域中的垃圾做标记
复制到空闲区域
进行内存整理操作,进行排序
对象区域和空闲区域进行角色翻转
特殊的数据移动至老生区
对象晋升策略
经过两次垃圾回收依然存活的对象
复制时,空闲区空间占用超过 25%
新生代中采用 Scavenge 算法
复制操作需要时间成本
因此新生区的空间被设置得比较小
Cheney 算法
标记-清除(Mark-Sweep)算法
标记阶段
从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
清除标记未垃圾的数据
会产生大量不连续的内存碎片
导致大对象无法分配到足够的连续内存
标记-整理(Mark-Compact)算法
其他步骤都一致
让存活对象向一段移动
清理掉端边界以外的内存
引用计数(Peference Counting)算法
问题比较多,很少使用了
比如循环引用
流程
当声明了一个变量并且将一个引用类型赋值给该变量的时候这个值的引用次数就为 1
如果同一个值又被赋给另一个变量,那么引用数加 1
如果该变量的值被其他的覆盖了,则引用次数减 1
当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,回收空间,垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的内存
全停顿
V8 是使用副垃圾回收器和主垃圾回收器处理垃圾回收
JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。我们把这种行为叫做全停顿(Stop-The-World)。
为了降低老生代的垃圾回收而造成的卡顿,V8 将标记过程分为一个个的子标记过程,同时让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成,我们把这个算法称为增量标记(Incremental Marking)算法
参考
「硬核JS」你真的了解垃圾回收机制吗
一文搞懂V8引擎的垃圾回收
收藏
收藏
0 条评论
下一页