当前位置: 首页 > 科技观察

GCRoot无法访问的对象会立即被垃圾回收吗?

时间:2023-03-13 13:19:04 科技观察

这道题是我在看牛客的面经时遇到的,特地整理在我的正经面试题文档中,所以这道题主要考察finalize方法的影响。Java提供了finalize方法可以帮助我们释放资源,类似于C++中的析构函数。这篇文章对此进行了解释。1、为什么会受影响我们都知道一个对象GCRoot是不可达的,java虚拟机认为它是一个垃圾对象,它会进行垃圾回收,但是如果这个对象包含finalize函数,性质就不一样了。为什么不同?java虚拟机在进行垃圾回收时,一看到这个对象类包含finalize函数,就会把这个函数交给FinalizerThread处理,包含这个finalize的对象就会加入到FinalizerThread的执行队列中,并使用链表将这些最终确定的对象串在一起。他的影响是只要不执行finalize,这些对象就会一直存在于堆区,但是这里只有4个包含finalize的对象,影响没有那么大。如果有10,000或100,000怎么办?这个影响大。finalize的原理其实很简单,这里简单总结一下:(1)在初始化过程中,对象会通过判断has_finalizer_flag和RegisterFinalizersAtInit这两个字段标志来判断是否重写finalize。(2)如果finalize被重写,将当前对象注册到FinalizerThread的ReferenceQueue队列中。注册后的对象称为Finalizer。方法是调用register_finalizer函数。这时java虚拟机看到当前有这个对象的引用,所以不进行垃圾回收。(3)对象开始被调用,FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象。开始执行finalize方法。在执行之前,这个对象一直在堆中。(4)对象执行完毕后,将Finalizer对象从队列中移除,java虚拟机看到该对象不再被引用,进行垃圾回收。这就是整个过程。但是这里主要看一下finalize方法对垃圾回收的影响。其实就是在第三步,也就是对象包含finalize,进入队列但是还没有被调用,会一直占用内存。注意:这实际上是一道面试题。看牛客网采访的时候,看到有人问。也就是说,GCRoot无法访问的对象会立即被垃圾回收吗?我们用一个案例来分析一波:2.案例演示我们创建一个类publicclassTestFinalizer{publicstaticclassFdd{//allocate1Mprivatebyte[]content=newbyte[1024*1024];@Overrideprotectedvoidfinalize(){System.out.println("finalizeisexecuted");}}publicstaticvoidmain(String[]args){for(inti=0;i<1000;i++){Fddfdd=newFdd();}}}现在已经创建了类,让我们设置参数。#最大堆内存-Xmx5m#最小堆内存-Xms5m#打印堆内存溢出错误-XX:+HeapDumpOnOutOfMemoryError#保存堆相关信息在以下路径-XX:HeapDumpPath=F:/a.dump是在main方法中创建的1000个Fdd对象,如果没有执行finalize方法,会因为没有调用而进行垃圾回收。这个时候,不管我们创建多少,都不会有问题。但是如果有finalize方法就不一样了。java.lang.OutOfMemoryError:JavaheapspaceDumpingheaptoF:/a.dump...finalize被执行finalize被执行finalize被执行finalize被执行.OutOfMemoryError:Javaheapspaceatcom.fdd.chapter2.TestFinalizer$Fdd.(TestFinalizer.java:6)atcom.fdd.chapter2.TestFinalizer.main(TestFinalizer.java:14)我们看到每个对象都会执行finalize,in执行前的时间会在堆区,执行完会被清理掉,所以这里可以看到finalize方法执行了不少于5次。但是一旦对象超过了我们设置的5M,就会出现内存溢出。一言以蔽之,有物聚。现在使用MAT工具对其进行分析。Mat工具是一个插件,你也可以自己下载一个。下载完成后,打开我们刚刚生成的a.dump。下图是分析的结果:a的内容是Finalizer,也就是我们的Fdd对象,b里面包含了很多乱七八糟的剩余信息。当然,你也可以查看一些其他的信息。全部在MAT工具上。还有一些正在执行的终结器和准备执行的终结器。OK,其他一些信息将不再显示。结论GCRoot无法访问的对象不会立即被垃圾回收。首先,它会判断是否包含finalize方法。如果是,先执行finalize方法。如果有很多这样的对象,那么这些对象将无法及时被GCRoot访问。它变得无用,会留在内存中,影响程序的效率。本文转载自微信公众号“愚公要移山”,可关注下方二维码。转载本文请联系愚公移山公众号。