gcroot java_一个GCRoot不可达的对象,会马上被垃圾回收吗?

1460000038946206

这个问题是我在刷牛客面经的时候遇到的,还特意整理在了个人常规面试题文档中,因此这道题主要考察的就是finalize方法的影响。java

java提供了一个finalize方法,能够帮助咱们进行资源释放,相似于C++中的析构函数。可是目前广泛的认识是不要使用,为何呢?就是由于对java虚拟机的垃圾回收有影响。这篇文章对其进行一个说明。面试

1、为何有影响

咱们都知道一个对象GCRoot不可达,java虚拟机就认为是垃圾对象,就会进行垃圾回收,可是若是这个对象包含了finalize函数,性质就不同了。怎么不同了呢?ide

java虚拟机在进行垃圾回收的时候,一看到这个对象类含有finalize函数,就把这个函数交给FinalizerThread处理,而包含了这个finalize的对象就会被添加到FinalizerThread的执行队列,并使用一个链表,把这些包含了finalize的对象串起来。函数

1460000038946204

他的影响在于只要finalize没有执行,那么这些对象就会一直存在堆区,不过这里只是4个包含了finalize的对象,影响不是那么大,若是有一万个或者是十万个呢?这就影响大了。工具

finalize的原理其实很简单,在这里简要的梳理一下:spa

(1)对象在初始化的过程当中会判断是否重写了finalize,方法是判断两个字段标志has_finalizer_flag和RegisterFinalizersAtInit。插件

(2)若是重写了finalize,那就把当前对象注册到FinalizerThread的ReferenceQueue队列中。注册以后的对象就叫作Finalizer。方法是调用register_finalizer函数。此时java虚拟机一看当前有这个对象的引用,因而就不进行垃圾回收了。线程

(3)对象开始被调用,FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象。开始执行finalize方法,在执行以前,这个对象一直在堆中。code

(4)对象执行完毕以后,将这个Finalizer对象从队列中移除,java虚拟机一看对象没有引用了,就进行垃圾回收了。对象

这就是整个过程。不过在这里咱们主要看的是finalize方法对垃圾回收的影响,其实就是在第三步,也就是这个对象含有finalize,进入了队列但一直没有被调用的这段时间,会一直占用内存。

注意:这里其实就是一道面试题,我在看牛客网上的面经时,看到有人被问到过。也就是GCRoot不可达的对象,会马上被垃圾回收吗?

咱们使用一个案例来分析一波:

2、案例演示

咱们建立一个类

`public class TestFinalizer {

public static class Fdd {

//分配1M

private byte[] content = new byte[1024*1024];

@Override

protected void finalize() {

System.out.println("finalize被执行");

}

}

public static void main(String[] args) {

for (int i = 0; i 

Fdd fdd = new Fdd();

}

}

}

`

如今建立了类,咱们设置一下参数。

`# 最大堆内存

-Xmx5m

最小堆内存

-Xms5m

堆内存溢出错误打印

-XX:+HeapDumpOnOutOfMemoryError

把堆相关信息保存在下列路径

-XX:HeapDumpPath=F:/a.dump`

在main方法中,建立了1000个Fdd对象,若是不执行finalize方法,那么由于没有调用因此会进行垃圾回收,此时不断咱们建立多少个,都不会出现任何问题。可是若是存在finalize方法,就不同了。

`java.lang.OutOfMemoryError: Java heap space

Dumping heap to F:/a.dump ...

finalize被执行

finalize被执行

finalize被执行

finalize被执行

finalize被执行

finalize被执行

finalize被执行

Unable to create F:/a.dump: File exists

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at com.fdd.chapter2.TestFinalizer$Fdd.(TestFinalizer.java:6)

at com.fdd.chapter2.TestFinalizer.main(TestFinalizer.java:14)`

咱们看到每一个对象都会执行finalize,在执行以前的这段时间一直会在堆区,执行完了就会被清理,因此你看到这里执行了很多于5次的finalize方法。可是对象一旦超出了咱们设置的5M,就会出现内存溢出。一句话总结就是出现了对象堆积。如今使用MAT工具来分析一下。

Mat工具是一个插件,也能够本身下载一个。下载完成以后打开咱们刚刚生成的a.dump便可。

下面这张图就是分析的结果:

1460000038946205

a这块的内容就是Finalizer,也就是咱们的Fdd对象,b包含的比较多,乱七八糟的剩余信息。固然你也能够查看一些其余的信息。都在MAT工具上。还有一些正在执行的finalizer和准备执行的。

1460000038946203

OK,一些其余的信息就再也不展现了。

结论

一个GCRoot不可达的对象,不会马上被垃圾回收,首先还会判断是否包含了finalize方法,若是有那就先执行finalize方法,若是这样的对象比较多,那么这部分对象及时GCRoot不可达,变得没用了,也会留在内存中,影响程序的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值