10_JVM 中哪些对象可以被垃圾回收
约 1250 字大约 4 分钟
2024-08-10
哪些变量引用的对象是不能回收的
JVM 中使用了一种可达性分析算法来判定哪些对象可以被回收,哪些不可以被回收
这个算法的意思是对每个对象分析,看一下哪些地方在引用他,然后一层一层往上去判断,看看是否有一个 GC Roots
我们以下面的代码为例,说明一下
public class Order {
public static void main(String[] args) {
loadMessageFromDisk();
}
public static void loadMessageFromDisk() {
MqManager mqManager = new MqManager();
}
}假设 MqManager 这个对象被局部变量引用着,此时新生代快满了,发生垃圾回收,就会去分析 MqManager 这个对象的可达性,如下图所示,然后就发现他被局部变量 mqManager 引用着,是不能回收的
在 JVM 规范中,局部变量可以作为 GC Roots 的,也就是说,只要一个对象被局部变量引用着,就说明他有一个 GC Roots,就不能被回收

还有另外一种常见的代码如下
public class Order {
private static MqManager mqManager = new MqManager();
}假设发生垃圾回收时,垃圾回收分析 MqManager 这个对象的可达性,就会发现他被类的静态变量 mqManager 引用着,是不能回收的
在 JVM 规范中,静态变量也可以看做是 GC Roots
一句话总结:只要对象被方法的局部变量、类的静态变量引用着,就不会回收
Java 对象的引用类型
Java 中的对象引用类型分为强引用、软引用、弱引用和虚引用
强引用(正常引用)
使用 new 关键字创建出来的对象就是强引用,定义强引用代码如下:
Object obj = new Object()obj 就是一个强引用了,如果一个对象具有强引用,那么垃圾回收器就不会回收
当 JVM 内存不足时,具备强引用的对象,虚拟机宁可抛出 OutOfMemoryError(内存空间不足) 使程序终止,也不会靠垃圾回收器去回收该对象来释放内存
当然,引用消失后还是会被垃圾回收
软引用
如果一个对象只具有软引用,JVM 内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存
只要垃圾回收器没有回收它,该对象就可以被程序使用,定义软引用代码如下:
SoftReference<Object> obj = new SoftReference<>(new Object());弱引用
弱引用是一种比软引用更弱的引用类型
当一个对象只被弱引用指向时,它可以被垃圾回收器回收,并且不会被放入任何队列中。当内存不足时,垃圾回收器会尝试回收弱引用指向的对象。定义弱引用代码如下:
WeakReference<Object> obj = new WeakReference<>(new Object());虚引用
虚引用是一种最弱的引用类型
当一个对象只被虚引用指向时,它可以被垃圾回收器回收,并且不会被放入任何队列中
与软引用和弱引用不同的是,虚引用的主要作用是在对象被回收之前执行一些清理操作。定义虚引用代码如下:
PhantomReference<Object> obj = new PhantomReference<>(new Object(), new PhantomReferenceQueue());finalize() 方法
Java 中的 finalize() 方法是 Object 类自带的一个方法,可以在子类重写 finalize() 方法,使用的目的一般是希望做一些对象销毁前最终的资源释放操作
当垃圾收集器检测到一个对象不可达时(不被任何线程中的任何对象所引用),若其是一个普通对象(未重写 finalize() 方法的对象),无需额外处理,进行回收即可
而若其是一个 Finalizer 对象(重写了 finalize() 方法的对象),则处理逻辑会很复杂,其首先会被 JVM 线程放进 Finalizer 队列(此时,可被该对象访问的其它对象,即便已经不可达,也都要随其暂时保留),然后在后期的某个不确定的时刻,JVM 线程再次将其从队列中取出,并调用其 finalize() 方法,完成后,其才真正可被垃圾收集器所回收
Java 里不建议使用 Finalizer:
何时执行无法保证
执行时出现未捕获的异常会被忽略
finalize()里边的实现可能会造成内存保留问题
Java 9已将Object类的finalize()方法废弃,作为替代,引入了Cleaner,虽然Cleaner比Finalizer强一点,类的所有者对其清理线程有一定的控制权,但Cleaner的执行仍由垃圾收集器所控制,所以Finalizer具有的无法保证何时执行等问题Cleaner同样具有,所以Cleaner同样不建议使用