1.GC垃圾回收算法
1.1可达性分析算法
实现过程:
1.可达性分析算法根据GC Root(根对象)作为起始点,按照从上到下的方式遍历确认GC Root->obj是否可达
2.每一条路径称为一条引用链,如果目标对象与GC Root没有任何引用链相连,则标记为不可达
3.在触发GC回收之前对象会自动调用内部的终止方法finalize(),该方法可以通过重写来复活该对象(只能调用一次)
4.当检测到finalize()方法并未建立与GC Root的引用链,则会在第二次标记后清除该对象
优点:
1.解决了引用计数器法中无法解决的循环引用的问题
2.不需要每个对象都分配一个计数器
缺点:
1.还是会产生内存碎片,当对象大量被回收后,内存碎片将可能会导致服务器的崩溃
算法具体过程如图所示:
- 从GC Root开始搜索,搜索过程中查询到Obj6和Obj7为不可达对象
- 标记不可达对象如下图的obj6和obj7,判断这两个对象是否在finalizer链表中,我们假设obj6有复活对象的操作,obj7无复活对象操作,但是都有finalize方法的重写
- 执行finalize方法后,复活的对象会重新生成一条引用链,而不能复活的对象则会直接被标记为可回收对象
- 第二次GC触发后,就会直接清除掉obj7,因为finalize方法只能执行一次
1.2引用计数器算法(已淘汰)
实现过程:
1.每个对象含有一个count值(计数器),初始值为1(自身引用)。
2.判断该对象是否被引用,引用则count值+1
3.若该对象引用失效后,则count值-1
4.直到count值=0则表示该对象无引用,为可回收对象,并进行清除
优点:
1.实现简单
2.垃圾对象便于识别
3.判断效率高,回收几乎无延迟
缺点:
1.每个对象都需要配备计数器,当对象过多时,增加了大量的存储空间开销
2.每次引用都需要更新计数器,更新计数器则会伴随着加减操作,增加了运算时间的开销
3.引用计数器无法实现循环引用,循环引用会造成OOM
算法具体过程如图所示:
-
为每个对象分配一个计数器count=1
-
当对象A被对象B引用,对象A的计数器则会+1;对象C的自身引用断开,则对象C的计数器则会-1
-
当检测到计数器count=0,则会触发垃圾回收,留下一块内存碎片
1.3 复制算法
问题:可达性算法已经将垃圾对象回收了,为什么还需要复制算法?
原因:可达性算法虽然可以解决引用计数法中无法解决的循环引用的问题,但是仍然会产生内存碎片,复制算法就是为了解决内存碎片问题而产生的。
实现过程:
1.当可达性分析算法执行完成后,复制算法首先会初始化堆,将Survival区一分为二,并命名为From和To
2.从GC Root开始遍历有向图,对每一个存活的对象进行复制(一个一个的复制),复制完成后对象的地址就会发生变化
3.然后就需要更新GC Root的引用地址
4.当所有对象遍历并更新到To空间以后,就需要对From空间的对象进行清除
5.清除后需要将From和To进行一次Swap(交换)
优点:
1.无内存碎片
2.吞吐量高,不需要遍历全堆,只需要处理存活对象
3.内存分配属于顺序分配,速度较快,只需要移动空闲指针(free pointer)
缺点:
1.堆利用率低,To区永远是一块用于准备下一次复制的空闲堆空间
算法实现步骤如下图所示:
-
可达性算法判断出Eden区的存活对象,并将垃圾收集后,复制算法开始将Survival区分为相等的两块Heap,并命名为From和To
-
其实复制算法并没有确定哪个是Eden区,哪个是From区,而是根据GC Root遍历搜索,每遍历一个对象就将它复制到To区,年龄此刻=1,复制进入后会建立新的引用链,这里位置较小,没画出来。
-
复制完成后,交换From和To的指向指针,如图所示:
-
当To区满了以后就会触发minor GC对该To区和Eden区进行一次垃圾回收,回收后触发复制算法,如下图,objc和obj1、obj2为垃圾,被回收了,复制算法复制的对象进入了To区,并将From和To进行交换。
5.幸存者区域的对象,直到年龄>=15以后就会直接丢进老年区
源码地址:https://github.com/kongwu-/gc_impl/tree/master/copying
1.4标记清除算法
实现过程:
1.当老年区内存空间不足,触发Major GC,会将可回收对象先进行标记(一次性标记)
2.标记对象就会直接被清除(一次性清除)
优点:
1.实现过程简单,由于在新生代中可达性分析过程中已经调用了一次finalize方法,此处无"缓刑"过程,标记即为可回收对象,直接被清除
缺点:
1.会产生内存碎片
2.效率慢,需要遍历两次,才能真正把垃圾清除
1.5标记整理算法
标记整理算法是根据标记清除算法中产生内存碎片问题而产生的
实现过程:
1.标记过程与标记清除算法一致(这个过程执行了标记和清除)
2.将标记存活的对象进行连续的内存分配(开始复制)
3.将存活对象分配完成以后,边界以外的对象全部清除(复制完成后将边界外对象清除)
优点:
1.解决了标记清除算法中存在的内存碎片化问题
2.相对复制算法提高了内存的利用率
缺点:
1.整理过程增加了一次遍历过程,效率更低了
2.整理过程需要一块可以连续存放所有对象的堆内存作为分配担保,但是并没有,因此不适用于对象过多的情况