图片 7

BAT面试必问题系列:JVM的判断对象是否已死和四种垃圾回收算法总结

Posted by

判断对象是否已死就是找出哪些对象是已经死掉的,以后不会再用到的,就像地上有废纸、饮料瓶和百元大钞,扫地前要先判断出地上废纸和饮料瓶是垃圾,百元大钞不是垃圾。判断对象是否已死有引用计数算法和可达性分析算法。

BAT面试必问题系列:深入详解JVM 内存区域及内存溢出分析

1.引用计数算法

(文末还分享更多Java架构面试专题等资料)!

给每一个对象添加一个引用计数器,每当有一个地方引用它时,计数器值加
1;每当有一个地方不再引用它时,计数器值减 1,这样只要计数器的值不为
0,就说明还有地方引用它,它就不是无用的对象。如下图,对象 2 有 1
个引用,它的引用计数器值为 1,对象 1有两个地方引用,它的引用计数器值为 2

判断对象是否已死就是找出哪些对象是已经死掉的,以后不会再用到的,就像地上有废纸、饮料瓶和百元大钞,扫地前要先判断出地上废纸和饮料瓶是垃圾,百元大钞不是垃圾。判断对象是否已死有引用计数算法和可达性分析算法。

图片 1

1.引用计数算法

这种方法看起来非常简单,但目前许多主流的虚拟机都没有选用这种算法来管理内存,原因就是当某些对象之间互相引用时,无法判断出这些对象是否已死,如下图,对象
1 和对象 2
都没有被堆外的变量引用,而是被对方互相引用,这时他们虽然没有用处了,但是引用计数器的值仍然是
1,无法判断他们是死对象,垃圾回收器也就无法回收。

给每一个对象添加一个引用计数器,每当有一个地方引用它时,计数器值加
1;每当有一个地方不再引用它时,计数器值减 1,这样只要计数器的值不为
0,就说明还有地方引用它,它就不是无用的对象。如下图,对象 2 有 1
个引用,它的引用计数器值为 1,对象 1有两个地方引用,它的引用计数器值为 2

图片 2

图片 1

2.可达性分析算法

这种方法看起来非常简单,但目前许多主流的虚拟机都没有选用这种算法来管理内存,原因就是当某些对象之间互相引用时,无法判断出这些对象是否已死,如下图,对象
1 和对象 2
都没有被堆外的变量引用,而是被对方互相引用,这时他们虽然没有用处了,但是引用计数器的值仍然是
1,无法判断他们是死对象,垃圾回收器也就无法回收。

了解可达性分析算法之前先了解一个概念——GC Roots,垃圾收集的起点,可以作为
GC Roots
的有虚拟机栈中本地变量表中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中
JNI(Native 方法)引用的对象。

图片 2

当一个对象到 GC Roots 没有任何引用链相连(GC Roots
到这个对象不可达)时,就说明此对象是不可用的,是死对象。

2.可达性分析算法

如下图:object1、object2、object3、object4 和 GC Roots
之间有可达路径,这些对象不会被回收,但 object5、object6、object7 到 GC
Roots 之间没有可达路径,这些对象就被判了死刑。

了解可达性分析算法之前先了解一个概念——GC Roots,垃圾收集的起点,可以作为
GC Roots
的有虚拟机栈中本地变量表中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中
JNI(Native 方法)引用的对象。

图片 5

当一个对象到 GC Roots 没有任何引用链相连(GC Roots
到这个对象不可达)时,就说明此对象是不可用的,是死对象。

上面被判了死刑的对象(object5、object6、object7)并不是必死无疑,还有挽救的余地。进行可达性分析后对象和
GC Roots
之间没有引用链相连时,对象将会被进行一次标记,接着会判断如果对象没有覆盖
Object的finalize() 方法或者 finalize()
方法已经被虚拟机调用过,那么它们就会被行刑;如果对象覆盖了 finalize()
方法且还没有被调用,则会执行 finalize() 方法中的内容,所以在 finalize()
方法中如果重新与 GC Roots
引用链上的对象关联就可以拯救自己,但是一般不建议这么做,周志明老师也建议大家完全可以忘掉这个方法~

如下图:object1、object2、object3、object4 和 GC Roots
之间有可达路径,这些对象不会被回收,但 object5、object6、object7 到 GC
Roots 之间没有可达路径,这些对象就被判了死刑。

3.方法区回收

图片 5

上面说的都是对堆内存中对象的判断,方法区中主要回收的是废弃的常量和无用的类。

上面被判了死刑的对象(object5、object6、object7)并不是必死无疑,还有挽救的余地。进行可达性分析后对象和
GC Roots
之间没有引用链相连时,对象将会被进行一次标记,接着会判断如果对象没有覆盖
Object的finalize() 方法或者 finalize()
方法已经被虚拟机调用过,那么它们就会被行刑;如果对象覆盖了 finalize()
方法且还没有被调用,则会执行 finalize() 方法中的内容,所以在 finalize()
方法中如果重新与 GC Roots
引用链上的对象关联就可以拯救自己,但是一般不建议这么做,周志明老师也建议大家完全可以忘掉这个方法~

判断常量是否废弃可以判断是否有地方引用这个常量,如果没有引用则为废弃的常量。

3.方法区回收

判断类是否废弃需要同时满足如下条件:

上面说的都是对堆内存中对象的判断,方法区中主要回收的是废弃的常量和无用的类。

该类所有的实例已经被回收(堆中不存在任何该类的实例)。

判断常量是否废弃可以判断是否有地方引用这个常量,如果没有引用则为废弃的常量。

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

判断类是否废弃需要同时满足如下条件:

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

该类所有的实例已经被回收(堆中不存在任何该类的实例)。

常用的垃圾回收算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。

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

1.标记-清除算法

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

分为标记和清除两个阶段,首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象,如下图。

常用的垃圾回收算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法。

缺点:标记和清除两个过程效率都不高;标记清除之后会产生大量不连续的内存碎片。

1.标记-清除算法

图片 7

分为标记和清除两个阶段,首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象,如下图。

相关文章

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注