前言
整理一下垃圾回收机制知识点,主要从下面几个方面来进行复习。
什么是垃圾回收机制?
垃圾回收机制的策略有哪些?
V8引擎对垃圾回收机制的优化?
垃圾回收机制
基本概念:通过自动内存管理实现内存分配和闲置资源回收。
实现思路:确定哪个变量不会再使用,释放它的内存
我们都知道一些变量和字符串是放在栈里面的,一些对象是放在堆里面的,这些都是需要占用内存的。
- 64位系统下能使用约
1.4GB
; - 32位系统下能使用约
0.7GB
.
所以要对那些不再使用的变量或者对象要进行回收,来为新生成的变量/对象腾出地方。
像C/C++语言来说他们是需要进行手动释放内存的。
java、python、javascript有一个垃圾回收器,来自动进行回收垃圾。
所以我们要懂浏览器的回收机制,来预防一些奇怪的内存泄漏问题。
垃圾回收机制的策略
标记清除
JavaScript中最常用的垃圾回收策略就是标记清理。
它的实现过程:
- 先全部设置标记。
- 接着浏览器从所有上下文开始检索 ,那些在上下文中被引用的,会把标记清除。
- 有标记的,垃圾回收程序做一些内存清理
缺点
清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了 内存碎片
。
当新创建了一个元素的话,分配内存空间,因为这个零散的空间而出现分配资源问题。
进行优化
标记整理法:
- 标记:和标记清除的标记过程一样,从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。
- 整理:让所有存活的对象都向内存的一端移动
- 清除:清理掉端边界以外的内存
这样的话就大大的整合了剩余的空间。
引用清理
实现过程:
- 给每个值记录它所被引用的次数
- 声明变量并赋给一个引用值,这个值引用数+1
- 当引用数为0的话,就会回收。
存在的问题
ini
function problem() {
let objectA = new Object();
let objectB = new Object();
objectA.someOtherObject = objectB;
objectB.anotherObject = objectA;
}
循环引用:所谓循环引用,就是对象A有一个指针指向对象B,而对象B也引用了对象A,这样的话他们永远也不会为0。
可以给他们赋空值来解除引用
ini
let element = document.getElementById("some_element");
let myObject = new Object();
myObject.element = element;
element.someObject = myObject;
要给他们赋空值
myObject.element = null;
element.someObject = null;
优化
大部分浏览器使用的标记清除,但也对它进行了优化,例如分代处理,并行回收,增量标记。
分代式垃圾处理
那种存活时间短的,小的,是新生代
存活时间长的,大的,就进入老生代。
新生代
新生代使用的垃圾处理机制:Scavenge
算法和并行回收
进行优化,具体如下:
新生代分为两个区域:使用区和空闲区
- 新加入的对象进入使用区
- 当使用区快满的时候就进行清理,对使用区的对象进行标记
- 那些存活的对象复制进入空闲区
- 复制之后空闲区和使用区调换身份,就是使用区变为空闲区,空闲区变为了使用区。
对象晋升策略:经过两次垃圾回收依然还存活的对象,会被移动到老生代中。
老生代
老生代使用的垃圾处理机制为:并发标记
。
具体实现:
- 老生代首先进行并行标记,开启一个辅助线程来进行标记操作。
- 之后进行并行清除操作,主线程清除的时候,辅助线程也会进行清除操作。
- 同时清除操作在主线程中也会采用增量的方法在线程中分步进行清除。
并行回收
先要搞清楚一个概念全停顿 :JavaScript 是单线程的,所以一旦执行垃圾回收算法,那正在执行的 JavaScript 脚本需要暂停下来,等垃圾回收完毕之后再恢复脚本执行。
当垃圾过多的时候,消耗的时间比较长的话,会堵塞Js的执行,那么就会造成页面卡顿,为了解决这个问题,就开出了辅助的线程来在后台进行垃圾回收,这样就不会影响主线程了。
新生代对象空间就采用并行策略,在执行垃圾回收的过程中,会启动了多个线程来负责新生代中的垃圾清理操作,这些线程同时将对象空间中的数据移动到空闲区域,这个过程中由于数据地址会发生改变,所以还需要同步更新引用这些对象的指针,此即并行回收。
增量标记
增量标记:就是将标记的步骤进行拆分,分为多步,接着js和垃圾回收他们进行交替运行。 优点:大大减少了主线程停顿的时间,让用户与浏览器交互的过程变得更加流畅
缺点:并没有减少主线程的总暂停的时间,甚至会略微增加