JVM垃圾回收机制(二)--何时回收

1.2 何时回收

前一章节已经明确了,在对象没有被引用的情况下,对象"可能"就要被回收了.

那么,我们怎么知道对象就没有引用了?当然,这里涉及到了算法.

我们介绍两种常用的算法实现:引用计数算法(Reference Counting Algorithm),可达性分析算法(Reachability Analysis Algorithm)

1.2.1 引用计数算法(Reference Counting Algorithm)

①对于引用计数算法,根据字面含义我们也能猜个八九不离十.所谓引用计数算法,就是建立一个与对象引用关联的计数器,在对象每次被引用的时候,计数器值+1,在引用指向作废时,计数器值-1.当计数器值为0时,表示该对象可以被回收了.

大家看的最多的估计就是周志明的深入理解Java虚拟机,里面只是提了一句面试时候大家都在说的一种说法:"给对象中添加一个引用计数器.....".

其实,这种说法有点片面,为什么这么说,因为引用计数器的实现有两种方式,一种是嵌入式(也可以叫侵入式),把引用计数器直接嵌入到对象内部.一种是非嵌入式(也可以叫非侵入式),就是另外开辟一块内存区域,专门存放对象的

引用计数器.

这里要提一句,目前主流的虚拟机基本没有采用引用计数算法去标记对象是否可以被回收(Python的内存管理机制使用了此种算法,还有Redis中).为什么没有被使用,最重要的原因在于使用引用计数算法可能会出现循环引用(A指向B,B又指向A)的对象不能被回收.

②怎么解决引用计数法中的循环引用对象没法回收的问题?

在知乎上,有人在面试时候被这么问.解答中分为三类,一类是避免使用循环引用,一类是在程序中使用弱引用代替强引用,一类是使用引用计算算法为主的垃圾收集器+辅助的使用"标记-清除"算法的tracing GC垃圾收集器去专门回收循环引用的对象.我们可以作为参考答案.

1.2.2 可达性分析算法(Reachability Analysis Algorithm)

①可达性分析算法,按照字面意思,就是分析对象通过某条线路是否还能到达这个对象.这里的线路就是引用链(Reference Chain),如A引用B,B引用C,从A开始寻找C,A-->B-->C这条线路就是引用链.

但是,分析总得有个开头,这个头就是我们可达性分析算法中的"GC Roots"对象.

合起来就是,我们通过"GC Roots"对象作为起点,向下搜索,只要通过引用链能找到对应的对象,那么这个对象就是可达的(也就是这个对象还被引用着,没有挂了).

②大家可能又会问,啥对象能做"GC Roots"对象啊?一般的,可以作为GC Roots对象的有那么4种:

一是在Java虚拟栈中,实际上是栈帧中的局部变量表中的reference类型(引用类型)引用的对象;

二是在类中定义的静态变量引用的对象(如public static String A="a";).

三是在方法区中常量引用的对象

四是在本地方法栈中本地方法调用(JNI,Java Native Invocation)中引用的对象.

另外补充一点,我们在1.2开头并没有说对象没有被引用就要被回收,而是用了"可能",为什么?

因为对象在没有引用的情况下,要经过标记和筛选过程才能确定对象是否要被回收.

第一次标记和筛选的过程:标记这个对象没有被引用了(无论使用哪种算法),但是是否要要回收还要看这个对象有没有重写Object类的finalize()方法或者finalize()方法是否被虚拟机调用过.如果对象没有重写finalize()或者已经执行过,那么此对象就要被回收了.

如果在筛选过程中,发现对象没有执行过finalize()方法且不存在被引用的情况,那么这个对象会被放在F-Quene队列中等待执行finalize()方法,而虚拟机会创建一个低优先级的名为Fianalier线程去执行.

第二次标记筛选的过程:当对象被放入F-Quene后,垃圾收集器会进行第二次标记,看是否有对象逃脱,这里说的逃脱就是有了新的引用.没有被重新引用的对象,将被回收.在F-Quene中逃脱的对象,在再次引用为0的时候,将直接回收.这点要注意.

jvm

相关推荐