认识V8
V8 是一款主流的 JavaScript 执行引擎
速度快,有一套优秀的内存管理机制
V8 采用即时编译
V8 内存设限
64 位操作系统不超过 1.5G,32 位操作系统不超过 800M
V8 垃圾回收策略
- 采用分代回收的思想
- 内存分为新生代存储区、老生代存储区
- 针对不同对象采用不同算法
V8 垃圾回收策略图示:
新生代对象指的是存活时间较短的对象,32M| 16M(64 位操作系统是 32M),存放在老生代区域
老生代对象指的是存活时间较长的对象,1.4G| 700M(64 位操作系统是 1.4G),存放在老生代区域
比如:全局存放的数据或者闭包存放的变量数据
V8 内存是设有上限的,基于这样的一个条件,需要采用分代回收的思想,然后不同代的对象,再去采用更适合的一个 GC 算法,去实现 V8 高效的垃圾回收的操作。
V8 中常用的 GC 算法
- 分代回收
- 空间复制
- 标记清除
- 标记整理
- 标记增量
V8 内存分配
- V8 内存一分为二
- 小空间用于存储新生代对象(32M | 16 M)
左侧白色区域存放新生代对象,右侧红色区域存放老生代对象
V8 如何回收新生代对象
- 回收过程中采用复制算法和标记整理算法
- 新生代内存区二个等大小空间
- 使用空间为 From,空闲空间为 To
- 活动对象存储于 From 空间
- 标记整理后将活动对象拷贝至 To
- From 与 To 交换空间完成释放
简单总结一下,新生代对象的存储区也被一分为二,而且是两个等大小的,在这两个等大小的空间中,一个 From 一个 To,当前我们使用的是 From,所有的对象声明都会放在这样的一个空间内,然后触发 GC 机制的时候,就会去把活动对象全部找到,然后进行整理,拷贝到 To 空间中,拷贝完成以后,再让 From 和 To 进行空间交换,那么原来的 To 就变成了 From,而原来的 From 变成了 To,这样一来,就算是完成了一个空间的释放和回收操作。
回收细节说明
拷贝过程中可能出现晋升,晋升就是将新生代对象移动至老生代进行存储
什么时候会触发晋升操作呢?
- 一轮 GC 还存活的新生代对象需要晋升
- To 空间的使用率超过25%
因为 To 会跟 From 交换变成 From,如果使用率过高,就存储不够用了
V8 如何回收老生代对象
- 主要采用标记清除、标记整理、增量标记算法
- 首先使用标记清除完成垃圾空间的回收
- 采用标记整理进行空间优化
- 采用增量标记进行效率优化
简单总结一下,针对于老生代对象的回收,采用的是标记清除、标记整理和增量标记三个算法,主要采用的是标记清除算法,去完成对应的垃圾空间的释放和回收,具体操作是找到老生代存储区当中的所有对象进行标记,然后直接释放掉垃圾对象的空间,显而易见会存在着一些碎片化的空间,在什么时候会去使用标记整理算法呢?如果当新生代存储区的内容向老生代存储区进行移动的时候,发现老生代存储区域的空间不足以来存新生代移过来的一些对象,就会进行标记整理。最后,还会去采用增量标记的方式,对当前回收的效率进行提升。
标记增量如何优化垃圾回收
垃圾回收运行时是会阻塞 JavaScript 程序运行的
增量标记简单的说,就是当前一整段的垃圾操作,拆分成多个小步骤组合的去完成,这样做的好处主要可以实现垃圾回收与程序执行交替的完成,而不是像以前需要停止程序执行去做垃圾回收。
一句话:让垃圾回收与程序执行交替进行
虽然看起来程序停顿了很多次,其实,即使垃圾达到最大 1.5G,V8 不采用增量标记进行回收也不超过 1s。
细节对比
新生代区域垃圾回收使用空间换时间
老生代区域垃圾回收不适合复制算法
V8 总结
V8 是一款主流的 JavaScript 执行引擎
V8 内存设置上限
V8 采用基于分代回收思想实现垃圾回收
V8 内存分为新生代和老生代
V8 垃圾回收常见的 GC 算法