V8垃圾回收的工作机制

502 阅读3分钟

前言

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。

V8引擎是一款主流的JavaScript执行引擎,目前Chrome浏览器和 nodeJS 平台都采用的V8。V8采用即是编译,速度比其他的js引擎快。V8内存设限,64位操作系统下一般不超过1.5G,32位不超过800M。

内存分配

V8采用的基于分代的垃圾回收,V8把内存空间分成了两个部分,新生代存储区和老生代存储区,

1.jpg

如图所示,左侧白色的区域是新生代存储区(64位的空间是32M、32位的空间是16M),用来存储新生代对象,这些对象是存活时间较短的对象。
右侧偏红色的是老生代存储区,用来存储老生代对象(64位的空间是1.4G、32位的空间是700M),用来存储新生代对象,这些对象是存活时间较长的对象。

新生代对象回收实现

新生代对象回收过程采用的是复制算法 + 标记管理,首先内存会区分为两个等大的小空间,如图所示的from和to空间,其中使用空间为from,空闲空间为To。代码在执行的过程中如果需要申请空间,活动对象就会存储在 from 空间。当 from 的内存增加到一定程度,标记整理把活动对象拷贝到 to 空间中,from 空间和to空间交换完成释放。
在拷贝的过程中可能会出现晋升,晋升就是把新生代对象移动到老生代存储区中,当一轮GC还存活的新生代需要晋升,当 To空间的内存使用超过25%,就会被这一批的活动对象都存储到老生代的存储空间。

老生代对象回收实现

新生代对象回收过程采用的是标记清除、标记整理、增量标记算法,首先使用标记清除完成垃圾空间的回收,这样会产生空间碎片,当新生代晋升的时候,老生代内存不足的时候,会触发标记整理进行空间优化。增量标记是用来进行效率优化的。

细节对比

新生代区域垃圾回收使用空间换时间,新生代的区域空间比较小,一分为二后空间更小了,虽然造成了少量的空间浪费,但是相对于时间上的提升是微不足道的,因此是用空间换时间
老生代区域垃圾回收不适合复制算法,相对于新生代,老生代的空间比较大,一分为二后会造成大量空间浪费,老生代存储的对象比较多和复杂,在复制的过程中消耗的时间也比较多。

标记增量如何优化垃圾回收

2.jpg

如图所示,当程序执行过程中,触发了GC操作,这时遍历对象进行标记,如果不使用标记增量,这段时间会标记对象的第一层,然后第二层(有的对象里面的一些元素是对象),然后第三次......最后进行清除操作,会阻塞很久,对用户体验不好。这时使用标记增量,会先遍历对象的第一层,程序执行,遍历第二层,程序执行......最后在进行清理,这样就不会阻塞很长时间,对用户比较友好。

总结

V8是目前主流的 JS 引擎,内存上有上限,采用新生代和老生代进行垃圾回收。新生代采用复制算法 + 标记管理的方式进行对象回收,用空间换时间。老生代是用的是标记清除、标记整理、增量标记算法进行对象回收,分批进行标记最后回收,提高了用户的体验