概念
垃圾回收器的作用是,跟踪监测已经分配的内存,发现不再被使用的内存,阶段性地进行回收。
在语言级别上引入自动垃圾回收算法,是避免内存泄漏的最佳方案。
算法
-
引用计数
每个对象内部维护一个整数值,用于该对象的引用计数:当对象被引用时引用计数加一,当对象不被引用时引用计数减一,当引用计数为 0 时,自动销毁对象。
缺陷有两个:一是每次对象赋值都要引用计数加一,增加了系统消耗。二是不能解决循环引用的问题。循环引用是指对象 A 和对象 B 互相持有对方的引用,这样两个对象的引用计数都不是 0 ,因此永远不能被收集。
目前引用计数法主要用在 c++ 、 PHP、python 中。
-
标记清除
该算法分为两步:标记和清除。 从程序的根节点开始递归地遍历所有对象,将能遍历到的对象打上标记,将所有未标记的对象当作垃圾销毁。
但是这个算法缺陷也有两个,一是STW 问题(Stop The World),算法在标记时必须暂停整个程序。 二是当程序中的对象逐渐增多时,递归遍历整个对象树会消耗很多的时间,在大型程序中这个时间可能会是毫秒级别的,让所有的用户等待几百毫秒的 GC 时间这是不能容忍的。
golang 1.5以前使用的这个算法。
-
三色标记
三色标记法是在标记清除法基础上的改进,它是一个并发的 GC 算法。
其原理是,创建三个集合:白、灰、黑,将所有对象放入白色集合。
然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入灰色集合。
之后遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合,最后收集所有白色对象(垃圾)
这个算法可以实现 “on-the-fly”,也就是在程序执行的同时进行收集,并不需要暂停整个程序。 但是也会有一个缺陷,可能程序中的垃圾产生的速度会大于垃圾收集的速度,这样会导致程序中的垃圾越来越多,不能及时地被收集掉。
使用这种算法的是 Go 1.5、Go 1.6。
-
分代收集
分代收集是传统标记清除法基础上的另一个改进,按照对象的生命周期长短来进行分代,然后给内存用量设定几个阀值,到达某个阀值时触发相应分代级别的回收。 一般都会分三代,在 java 中称之为新生代、年老代和永久代
原理:新对象放入新生代;当内存用量超过一个较小的阈值时,触发新生代的收集;新生代中幸存的对象(未被收集)放入老年代;只有当内存用量超过一个较高的阈值时,才会触发老年代的收集;永久代的收集同理。因为新生代中的对象十分少,所以新生代的收集会非常快(比年老代快几个数量级),只有内存消耗过于大的时才会触发较慢的年老代和永久代的收集。
使用的语言有 java、.NET 。