GC的基本思想是考察每个对象的可触及性(可达性),就是从GC Root开始是否可以访问到这个对象。如果可以,则可达,否则就是不可达。在Java中,可作为GC Roots的对象包括:
JVM栈(栈中的本地变量表)中的引用的对象
方法去中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(Native方法)引用的对象
对于可触及性,可以包含以下3种状态。
可触及的:从根节点开始,可以到达这个对象。
可复活的:对象的所有引用都被释放,但是对象有可能在finalize()函数中复活
不可触及的:对象的finalize()方法别调用过,并且没有被复活(finalize()方法只会被调用一次)
Java中提供了4个级别的引用:强引用、软引用、弱引用和虚引用。
强引用
强引用是程序中一般使用的引用类型。强引用的对象是可触及的、不可被回收的。例如:
上述代码内存示意图如下:
强引用包含以下特点:
可以直接访问目标对象
所指向的对象在恩和时候都不会被系统回收,JVM宁愿抛出OOM,也不会回收对象
可能导致内存泄漏
软引用
软引用是比强引用弱一点的引用类型。一个对象如果只持有软引用,那么当堆空间不足时,就会被回收(由SoftReference实现)。代码如下:
增加上述GC参数,运行结果如下:
综上,GC未必会回收软引用的对象,但是当内存不足时,软引用对象就会被回收,因此软引用对象不会引起内存溢出。
引用队列
每个软引用都可以附带一个引用队列,当对象的可达性状态发生变化时(由可达变为不可达),软引用对象就会进入引用队列。通过这个队列,可以追踪对象的回收情况。代码如下:
使用上述VM参数,执行结果如下:
弱引用
弱引用是一种比软引用还弱的一种引用。GC时只要发现,就会对其进行回收。一旦被回收时,就会加入到一个引用队列中(和软引用很像),Java中使用WeakReference实现。demo如下:
上述代码运行结果如下:
从结果看来,不论内存状况,每次GC都会清除弱引用的对象。
虚引用(幽灵引用)
虚引用是所有引用中最弱的一个。持有虚引用的对象,和没有引用几乎一样,随时都有可能被回收,因此也叫幽灵引用。当使用虚引用的get()方法获得强引用时,总是失败的。虚引用必须和引用队列一起使用,作用于跟踪垃圾回收过程。
下面示例使用虚引用跟踪一个可复活对象的回收。
上述代码执行结果如下:
参考:《实战Java虚拟机》