本文转载自微信公众号「程序员巴士」,作者七十一 。转载本文请联系程序员巴士公众号。
前言
经过前段时间一面的通过,阿巴阿巴被邀请进入二面,这次她与遇到的面试官将继续为难她,要问她关于GC算法的问题。
回家等通知
面试官: 你对JVM的垃圾回收了解吗?
阿巴阿巴: 嗯嗯,了解一些。
面试官: 那么JVM是如何判断一个对象是垃圾呢?
阿巴阿巴: 好像有一个可达性分析法。
阿巴阿巴: 就是对象可达会判定为活对象,然后不可达的就当作“垃圾”。
面试官: 嗯....讲一下你了解的垃圾回收算法吧。
阿巴阿巴:
标记清楚算法
标记整理算法
复制算法
分代收集算法
面试官: 嗯....那你对这些算法了解吗?
阿巴阿巴: 嗯....不太了解...
面试官: 行,今天先面到这里,你这边先回去等通知吧??
阿巴阿巴: 好的。
很遗憾,您未能通过面试,您的简历已加入公司人才库,期待下次相遇......
当场拿Offer
面试官: 你对JVM的垃圾回收了解吗?
阿巴阿巴: 嗯嗯,了解一些。
面试官: 那么JVM是如何判断一个对象是垃圾呢?
阿巴阿巴: 有两种方法,一种是引用计数法,另一种是可达性分析法。
阿巴阿巴: 引用计数法就是给对象一个引用计数器,每当有引用引向该对象时,引用计数器就加一,每当有引用断开的时候,引用计数器就减一,这样当引用计数器为零时,那么就认为这个对象已经没有用了,也就是所谓的“垃圾”,但是这种方式有个很大的弊端,对于循环引用无法处理。
阿巴阿巴: 循环引用的对象外部引用存在的情况,这种情况看似没啥问题,但是当我们把方法区的引用断开时,问题就暴露出来了。
阿巴阿巴: 循环引用的对象外部引用断开的情况。
阿巴阿巴: 上面这种引用断开的情况,显然对象A和对象B已经没有外部引用来引用它们,它们已经成为了垃圾,而引用计数器因为它们相互引用(循环引用),其值都为1,导致无法被回收,这个弊端导致引用计数法实际并没有在JVM中所使用。
阿巴阿巴: 可达性分析法就是通过GC Roots的对象,以它为根往下搜索,这条被搜索的路径称为“引用链”,当一个对象不被任何GC Roots的引用链所链接,那么就判定这个对象已经“死了”,我们一般称这个对象“不可达”。
面试官: 你刚有提到GC Roots,那你知道哪些对象可以作为GC Roots的对象吗?
阿巴阿巴: 嗯嗯了解,主要有以下四类对象可以作为GC Roots的对象。
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
阿巴阿巴: 下面这张图可以直观的看出它们的关系。
阿巴阿巴: 可以看出,只有被引用链链上的对象才能被判定为“存活”,而不在引用链上的对象则被判定为“死亡”,也将作为垃圾被回收。
面试官: 讲的很不错,那垃圾回收除了回收堆中的对象外,方法区中会有垃圾被回收吗?
阿巴阿巴: 方法区中也是有垃圾回收的,方法区中主要回收废弃了的常量和无用的类。
面试官: 嗯....讲一下你了解的垃圾回收算法吧。
阿巴阿巴: 垃圾回收算法主要有以下四类。
- 标记清楚算法
- 标记整理算法
- 复制算法
- 分代收集算法
阿巴阿巴: 标记清楚算法,是分为2个阶段的,第一个阶段进行“标记”,第二个阶段进行“清除”,先标记出所有要清除的对象,也就是灰色部分,然后进行回收。
阿巴阿巴: 采用标记清除算法对堆进行垃圾清理后,产生了很多空间碎片,这些空间碎片使新对象的内存分配造成困难,不仅如此,标记清除算法在标记阶段和清除阶段的效率都不太高。
阿巴阿巴: 标记整理算法孕育而生,解决了过多内存碎片的问题。
阿巴阿巴: 为了解决效率的问题,复制算法也出现了,即把一块内存分成大小相等的2块,每次使用的时候只使用其中的一块,当一块内存使用完的时候,把这块内存中存活的对象转移到另一块内存中,然后将这块内存中的对象全部清空。
阿巴阿巴: 复制算法实现简单、方便且效率很高,也不需要考虑内存碎片的问题,但是要将内存缩小为原来的一半,这代价无疑很高。
阿巴阿巴: 而且新生代的对象大多数都是朝生夕死的,按照1:1的空间比例来使用复制算法,将极大的影响了内存的性能。
阿巴阿巴: 分代算法即将堆区进行划分,然后根据不同区域的情况来适用相应的垃圾回收算法。
阿巴阿巴: 下图是对新生代的细化,新生代分成Eden区和survivor区,其中survivor区又分为(s0和s1)俩个区域,它们的比例如图所示为8 : 1 : 1。新对象优先会在Eden区进行分配,标记清除算法在这里不适用,因为碎片太多,如果没有连续的足够空间来分配给对象,又会继续触发垃圾回收,对性能影响比较大。
阿巴阿巴: 对于传统的GC来说,都无法避免GC过程中带来的“STOP-THE-WORLD”,我们一般简称STW,STW对系统性能的影响很大,那么如何消除STW或者减少STW的时间显得尤为重要,其实分代算法并非是一种具体的算法,和前面的标记清除、标记整理算法、复制算法不同的是,分代算法只是对对堆得一个划分,然后在不同区域使用不同的算法,从而将STW的时间细分到各个区域,使得STW时间不会持续很长一段时间,来达到提高系统性能的目的。
面试官: 讲的很清楚细致了,很不错,明天来上班吧??。
总结
关于垃圾回收算法这一块,一定要答到GC Roots,以及各种垃圾回收算法,及他们的优点和缺点。