垃圾回收相关算法概述

垃圾回收相关算法概述

垃圾标记阶段:对象存活判断:在堆中存放的几乎所有的Java对象实例,在GC执行垃圾回收之前,首先需要区分内存中哪些对象是存活对象,哪些对象是已经死掉的对象,只有标记为已经死亡的对象,GC才会在执行垃圾回收时,释放掉其所占用的内存空间,这个过程我们称为垃圾标记阶段,判断对象存活一般有两种方式:引用计数算法和可达性分析算法。

1-垃圾标记阶段的算法之引用计数算法

1.引用计数算法比较简单,对每个对象保存一个整型的引用计数器属性,用于记录对象被引用的情况。
2.对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减一。只要对象A的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。

优点:实现简单,垃圾对象便于辨识,判定效率高,回收没有延迟。

缺点:1.它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。2.每次赋值都需要重新更新计数器,伴随着减法和加法操作,这样增加了时间开销。3.引用计数器有一个严重的问题,即无法处理循环引用,这一条是致命缺陷,导致在Java的垃圾回收器中没有使用这类算法。

下面举一个循环引用的例子:当堆中的对象相互引用时,此时引用计数器永远不为0,,则永远不会被回收。
垃圾回收相关算法概述_第1张图片

2-垃圾标记阶段的算法之可达性分析算法

1.可达性分析算法是以根对象集合(GC Root)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。所谓的根对象集合就是一组必须活跃的引用,例如栈中的局部表量表里的引用。
2.使用可达性分析算法后,内存中存活对象都会被根对象直接或间接连接着,搜索走过的路径称为引用链
3.如果对象没有任何引用链相连,则是不可达的,就意味着该对象已经死亡,可以标记为垃圾对象。
4.在可达性分析算法中,只有能够被根对象集合或者间接链接的对象才是存活对象。
如下图所示:
垃圾回收相关算法概述_第2张图片

3-对象的finalization机制

1.当垃圾回收器发现没有引用指向一个对象时,即垃圾回收器回收此对象之前,总会先调用这个对象的finalization()方法。

2.finalization()方法允许在子类中被填写,用于在对象被回收时进行资源释放,通常在这个方法中进行一些资源和清理的工作,比如关闭文件,套接字和链接数据库等。

3.永远不要主动去调用某个对象的finalization()方法,应该交给垃圾回收机制调用,因为(1).在finalization()时可能会导致对象复活。(2).finalization()方法的执行时间是没有保障的,它完全由GC线程决定,极端情况下若不发生GC则finalization()方法将没有执行机会。

4.由于finalization()方法的出现,虚拟机中的对象可能有三种状态。如果从所有的根节点都无法访问到某个对象,说明该对象已经不再使用。一般来说,此对象需要被回收。但事实上,也并非是“非死不可的”,这时它们暂时处于“缓刑”阶段。三种状态如下:
可触及的:从根节点开始,可以到达这个对象。
可复活的:对象的所有引用都被释放,但是对象重写了finalization则有可能在finalization()方法中复活。
不可触及的:对象的finalization()被调用,并且没有复活,那么就会进入不可触及状态。不可触及状态不可能被复活,因为finalization()方法只会被调用一次,倘若某个对象被复活后又没有引用指向它则finalization方法不会被调用也就会直接到达不可触及状态。
具体过程我们来分析一下:判定一个对象objA是否可回收,至少要经历两次标记过程:
1.如果对象objA到根集合对象没有引用链,则进行第一次标记。
2.进行筛选,判断此对象是否有必要执行finalization方法。
(1)如果对象objA没有重写finalization方法或者finalization方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA被判定为不可触及的
(2)如果对象objA重写了finalization方法,且还未执行过,那么objA对象会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalization线程触发其finalization方法执行。
(3)finalization方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记,如果objA在finalization方法中与引用链上的任何一个对象建立了链接,那么在第二次标记的时候,objA会被移出“即将回收集合”。之后,对象若再出现没有引用存在的情况,finalization方法不会被再次调用,对象会直接变成不可触及状态,也就是说一个对象的finalization方法只会被调用一次。

4-垃圾清除阶段的算法之标记-清除(Mark-Sweep)算法

执行过程:当堆中的有效内存空间被耗尽时,就会停止整个程序,然后进行两项工作,第一项是标记,第二项是清除。
标记:从根节点开始遍历,标记所有被引用的对象,一般是在对象的Heather中记录为可达对象。
清除:对堆内存从头到尾进行线性的遍历,如果发现某个对象在其Header中没有标记为可达对象,则将其回收。

缺点:1.效率不算高;2.在进行GC时,需要停止整个程序,导致用户体验差;3.这种方式清理出来的空闲内存是不连续的,会产生内存碎片,需要维护一个空闲列表。

5-垃圾清除阶段的算法之复制(Copying)算法

为了解决标记-清除算法在垃圾收集效率方面的缺陷,M.L.Minsky于1963年发表了著名的论文,使用双存储器的垃圾回收。
核心思想:将活着的两块内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中所有的对象,交换两个内存的角色,最后完成垃圾回收。在堆中Eden区中的幸存者区(S0,S1)就采用了此算法如下图所示:
垃圾回收相关算法概述_第3张图片
优点:(1)没有标记和清除过程,实现简单,运行高效。(2)复制过去以后保证空间的连续性,不会出现碎片问题。
缺点:(1)此算法的缺陷也是十分明显,就是需要两倍的内存空间。(2)复制的对象最好不要太多,不然也不会很理想。

6-垃圾清除阶段的算法之标记-压缩(Mark-Compact)算法

复制算法的高效性是建立在存活对象少,垃圾对象多的情况下。这种情况在新生代经常发生,所以新生代最适合复制算法。但是在老年代中,更常见的是大部分对象都是存活对象,如果依然使用复制算法,由于存活对象比较多,复制的成本也会很高,因此,基于老年代垃圾回收的特性,需要使用其他的算法
标记-清除算法的确可以应用在老年代中,但是该算法效率低下,而且执行完后悔产生内存碎片,所以JVM的设计者们在此基础上进行改进,标记-压缩算法由此而来。
垃圾回收相关算法概述_第4张图片
标记-压缩算法的最终效果等同于标记-清除算法执行完成后,再进行一次内存碎片整理,因此也可以把它称为标记-清除-压缩算法。
优点:(1)消除了标记-清除算法当中,内存区域分散的缺点。(2)消除了复制算法当中,内存减半的高额代价。
缺点:(1)从效率上来说,标记-整理算法要低于复制算法(2)移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址。

7-7种经典垃圾回收器总结

垃圾回收相关算法概述_第5张图片
好文章:https://www.6aiq.com/article/1570200567790

你可能感兴趣的:(垃圾回收,垃圾回收,算法)