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
同样不建议使用