本文内容来自于对《垃圾回收的算法与实现》内容的总结算法
V8是啥编程
这个你们都知道,V8 全称是V8 JavaScript Engine
,一个用C++写的JavaScript
引擎。markdown
垃圾回收又是啥编程语言
垃圾回收的英文是 Garbage Collection
,简称GC
。在代码运行的过程当中,全部的数据都会存放在内存空间,若是没有GC
,开发者就必须手动进行内存管理,否则总有一天内存会被占满,进而致使进程崩溃,甚至系统崩溃。GC
的做用就是让计算机自动帮开发者进行内存管理,把内存中不须要再次使用的垃圾回收掉。函数
本文讲的只是V8
中的垃圾回收算法,而目前已有的垃圾回收算法远不止这几种,并且目前没有完美的垃圾回收算法。spa
GC
时,相应的对象会被销毁或者保留。这时候咱们能够经过对象的指针去搜寻其余对象。设计
指针通常指向对象的首地址。其实能够简单理解为JS中的引用地址。3d
指动态存放对象的内存空间。JS中,对象存放在堆中。指针
mutator
的实体是“应用程序”code
我的理解,在JS的场景下,它指的就是V8引擎。在V8运行的过程当中,会不断的去生成对象,修改对象之间的引用关系(即更新了指针)。这些改变就会带来垃圾,这时候就须要GC
。
活动对象指堆中的能被mutator引用的对象。
非活动对象指堆中的不能被mutator引用的对象,即内存垃圾。
在下文中,屡次说起的一个名词是根。什么是根呢?术语上就是能够经过mutator直接引用的对象。举个例子
const obj = new Object(); // A对象
obj.child = new Object(); // B对象
复制代码
第一行代码中,咱们建立了一个对象A,它的引用地址赋值给了obj
。第二行代码中,咱们又建立了一个对象,它的引用地址赋值给了obj.child
。那么此时堆以下图所示 这里因为咱们能够经过全局变量
obj
找到A,因此A是活动对象,而后咱们又能够经过A找到B,因此B也是活动对象。那么这个全局变量obj
就是一个根。
将内存空间中的对象分为两类,新生代和老年代。新建立的对象存放在新生代中,通过新生代中的GC
后,某些对象依然存活。将存活了数次的对象看成老年代对象来处理。
新生代对象->老年代对象的过程,咱们称之为晋升。
GC
的分类GC
有两种类型,保守式和准确式。
GC
(Conservative GC)当GC
时不能识别一个东西是否是指针时,这个时候的根被称为不明确的根。
举个例子,咱们定义了全局变量a
和局部变量obj
,a
是一个数值,obj
是一个指针(引用地址),引用地址跟值a
同样,这个时候GC
很难分辨出a
究竟是指针仍是值。因而,保守处理,把它当成一个指针,当obj
指向的对象应该被垃圾回收时,因为全局变量a
的存在,它不会被垃圾回收。这就是保守式GC
。
window.a = 0x00d0caf0; // 伪代码,固然是会报错的
const obj = new Object(); // 建立了对象,地址为0x00d0caf0
复制代码
在保守式GC
的场景下,对象不可以被移动。由于若是移动了对象,意味着对象的引用地址会发生变化,那么上面的obj
相应的会重写成移动后的引用地址,与此同时,全局变量a
也会被重写,这就很是恐怖了。因此对象不可以被移动。
GC
(Exact GC)顾名思义,准确式GC
可以正确识别出哪些内容是值,哪些内容是指针。要实现准确式GC
,依赖于编程语言的处理,意味着成本的增长,这里再也不赘述。
V8
中的GC
V8
中实现了准确式GC
。
GC
算法方面采用了分代垃圾回收,结构以下。
GC
复制算法(By Cheney)GC
复制算法将内存空间分为From
和To
,当From
空间占满时,将From
空间中的活动对象(划重点)复制到To
空间中,非活动对象回收掉,而后From
和To
互换。显而易见,From
和To
空间的大小要彻底一致。
GC
复制算法有不少种,好比 Robert R.Fenichel 和 Jerome C.Yochelson研究出来的和 C. J.Cheney 研究出来的。下面介绍的是Cheney研究出来的算法。
在Cheney的复制算法中,算法流程以下
初始状态
首先,复制全部从根直接引用的对象,B
和 G
。注意,新的B
引用了From
中的A
,新的G
仍是在引用From
中的B
(为区分,写做B1
)和E
。
而后,搜索B1
,发现引用了A
,因而把A
复制到To
中,同时修正B
中的指向。
接着,搜索G
,把E
复制到To
中,而且G
指向B1
的指针换到了B
。
最后,搜索A
和E
,发现没有引用的对象,清空From
,将From
和To
空间互换,复制算法结束。
GC
,由于移动了对象From
空间没有分块的时候
GC
标记-清除算法本算法分为两个阶段
很明显,通过这两个阶段后,不能利用的内存空得以再次被利用。
GC
的场景屡次GC
后会致使内存中出现碎片。碎片化的后果是,即便可用内存的总空间够用,也会由于单个空间不够用致使不可以分配内容(这个时候就要用到下面提到的标记-压缩算法了)
假设内存空间一共5KB,下图中A、B、C、D、E各占了1KB,经历一次GC
后,B和D被回收,内存空间中剩余2KB,此时分配一个大小为2KB的对象到内存空间中,没法分配,由于剩余的2KB空间并不连续。
GC
时会检查)GC
时的晋升)GC
标记-压缩算法标记阶段:V8
采用深度优先的方式进行标记,即标记了对象,随后会去标记这个对象的子对象。深度优先遍历时,通常采用递归操做,递归时,天然须要用到栈,在V8
中,这个栈由V8
自行生成。栈所用的空间是新生代的From
空间。由于老年代GC
以前,必然会执行新生代GC
,这个时候From
空间是空的,既然空都空了,不如就把栈放在这里,不用白不用xd。
压缩阶段:
压缩前,能够看到老年代空间中存在不少空白小方块,即内存碎片
压缩时,将内存对象按顺序逐个移动到内存空间的前面
压缩后,能够看到空白小方块已是连续的了,不存在内存碎片
一开始的时候,标题是《垃圾回收算法》,然而。。。了解深了以后发现,除了上面这些,还有引用计数法,增量式垃圾回收,RC Immix算法等等。即便是相同算法,不一样设计者也有必定的区别。不一样语言的GC
算法实现上也有必定的区别。内容多的离谱,因而在标题前面加上了“V8中的”。
这个文涉及的东西实际开发中并不常见,并且术语比较多,不免有地方写错了,有朋友发现了的话,麻烦评论区留个言。