JVM(三):GC详解之垃圾回收算法

参考周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》

推荐去看该书籍,讲得很好。


在讲回收算法之前,先说一下怎么样的对象才需要进行回收:

1.引用计数算法

这个算法的实现思路是:给对象添加一个引用计数器,有引用这个对象的时候,计数器+1;引用失效的时候-1;计数器为0的时候该对象是不能被使用了。

但是虚拟机并不是通过这个算法来判断对象的是否存活


2.可达性分析算法

这个算法的实现思路是:通过“GC Roots”的对象作为起始点,从该节点往下搜索,搜索的路径称为引用链;

如图:画得丑了点,不要介意


没有引用链的话(就是说从GC Root到该对象不可达),就会判定是可回收的对象。

可作为GC Root的对象有:

1)虚拟机栈中引用的对象;

2)方法区中类静态属性引用的对象;

3)方法区中常量引用的对象;

4)本地方法栈中native方法引用的对象


关于引用参考我另一篇博客:强引用,软引用,弱引用,虚引用


方法区的回收

对于方法区的常量,如果经常对其进行回收的话,效率会很低。

而永久代的垃圾回收主要是对废弃常量无用的类进行回收

废弃的常量:指的是在常量池中的常量没有被其他地方引用。如果发生了内存回收,而且有必要的情况下,这些常量会被回收。

无用的类:需要满足三个条件

①该类的所有实例都被回收了。

②加载该类的ClassLoader已被回收。

③该类对应的java.lang.Class对象没有被引用,而且无法在任何地方通过反射访问此类的方法。


垃圾回收算法

一、标记-清除算法


如图所示:首先会先标出可回收的的对象,然后在标记完后统一的回收这些被标记的对象。

如何标记呢?一个对象要被判“死刑”还是至少要进行2次的标记的:如果该对象在进行可达性分析后没有引用链,那么它会被进行第一次的标记和筛选。

筛选指的是是否有必要执行finalize()方法;如果该对象没有覆盖该方法或者已经被虚拟机调用过了都是没有必要执行的情况。在这里我不推荐去覆盖这个方法哦。


这个算法有两个缺点:①效率很低。②标记的对象清除后会产生大量的不连续的内存空间,如果有需要分配一块比较的内存控件的对象时,这个时候如果连续的空间不够的时候还需要再进行一次垃圾回收。


二、复制算法



如图所示:先是按内存的容量划分成两块相等的,每一次只使用一块,当使用的这块内存用完后,会先将未标记(存活)的对象复制到另一份内存中,然后将这块内存都清理回收掉。可想而知,如果存活率比较高,那么复制操作就会有很多,导致效率就会很低了。


三、标记-整理算法


如图:前面的操作跟标记-清除算法差不多,都是先标记可回收的对象,然后不同的就是让所有存活的对象都向一端移动,然后清理掉端边界以外的内存(就是可回收对象的这块内存)


四、分代收集算法

意思就是:把Java的堆分为新生代和老年代进行回收。

新生代的对象存活率比较低,所以可以采用复制算法。

老年代的对象存活率高,没有额外空间进行分配担保,所以就需要标记-清除或者标记整理算法来回收内存。



猜你喜欢

转载自blog.csdn.net/adrian_dai/article/details/80255681