如果参加Java研发的面试,很容易被面试官问到“说说你对gc回收有什么了解”,所以今天整理一下这部分内容。
首先要解决的一个问题是如何判断哪些Java对象是可以回收的。这就要说到根搜索算法gcroots了,实现这个算法首先要构造树状结构,将对象作为节点,将调用关系作为分支。能用来做根节点的对象有虚拟机栈中引用的对象、方法区中静态变量引用的对象、方法区中常量引用的对象、本地栈中JNI引用的对象。这些根节点的对象调用的对象可以向下形成分支,维护一个动态的树状结构。
可以通过遍历这个树状结构,找到不再被引用的对象,可以对这些对象进行回收。因为这个树状结构是动态改变的,所以要找到一个线程执行完毕的安全点来生成一个像是快照功能的临时静止树状结构,进行遍历工作。以上是根搜索算法,还有一种不太经常被使用的引用计数器算法。
这个就比较好理解了,每个对象维护一个计数器,如果对象被引用就+1,用完了就-1。所以当技术器归零的时候,这个对象不再被引用了,就可以回收了。这种方法存在一个缺陷就是循环引用,A引用B,B又引用A,就会陷入一个僵局。想要解决的话,可能要通过一些同步互斥的设置。
解决完了哪些Java对象可以回收,就可以进入到了解回收算法的步骤了。标记清除算法采用的是把存储区域里可以清除的对象第一次先标记出来,然后第二次清除的思路,碎片化问题比较严重。复制算法采用的是把存储区域一分为二,把空白的块复制到要清除的块的位置,改善了碎片化的问题,但是可用空间变得有限了。标记整理算法采用的是把被标记的对象挪到一起,空白对象空间前向归置,中间设置界限,界限外的被清除。分代收集算法就要把对象分成新生代(清除多,采用复制算法)和老年代(清除少,采用标记清除算法)来处理。
最后就是归纳一下gc垃圾收集器:Serial(单线程、复制),ParNew(Serial的多线程版本),Parallel Scavenge(多线程、复制),Serial Old(单线程、标记整理),Parallel Old(多线程、标记整理),CMS(多线程、标记清除),G1(不分新老、标记整理、低停顿)