这个问题是我在磨牛客面经的时候遇到的,特意整理成个人的常规面题文件,所以这个问题主要考察了finalize方法的影响。 java
java提供了finalize方法来帮助释放资源,就像c中的析构函数一样。 但是,现在被广泛认识的是不使用。 为什么会这样呢? 因为这会影响java虚拟机的垃圾回收。 这篇文章就此进行说明。 面试
1、为什么会有影响
我们知道java虚拟机会将其识别为垃圾对象,进行垃圾回收的对象GCRoot无法到达,但是如果此对象包含finalize函数,则性质会不同。 怎么不一样? ide
java虚拟机进行垃圾回收时,如果看到此对象类包含finalize函数,则将此函数传递给FinalizerThread,而包含此finalize的对象是FinalizerThread 函数
除非finalize运行,否则这些对象一直存在于堆中,但这里只有四个对象包含finalize,影响并不大。 如果有一万或十万人呢? 这有很大的影响。 工具
finalize原理其实很简单。 现在让我们简单地整理一下。 spa
)1)对象在初始化过程中判断是否改写了finalize,判断两个字段标志has_finalizer_flag和RegisterFinalizersAtInit。 插件
)2)改写finalize时,将当前对象注册到FinalizerThread的参考队列中。 注册后的对象称为Finalizer。 方法是调用register_finalizer函数。 此时,java虚拟机当前对该对象有引用,因此不进行垃圾回收。 线程
)3)对象开始调用,FinalizerThread线程从ReferenceQueue队列中检索Finalizer对象。 开始执行finalize方法。 在运行之前,此对象位于堆中。 代码
)4)对象执行完成后,将此Finalizer对象从队列中删除,java虚拟机认为对象未被引用,然后进行垃圾回收。 对象
这就是整个过程。 但是,现在我们主要看看finalize方法对垃圾回收的影响。 实际上,第3步,即此对象包含finalize,它会在排队但未调用时继续占用内存。
注意:这里其实是面条问题。 看着牛客网上的面经,有人被问到过。 也就是说,GCRoot收不到的对象,会马上被垃圾回收吗?
用案例分析一下波浪吧:
2、案例演示
组成一个班吧
` publicclassTestFinalizer {
publicstaticclassFdd {
分配//1m
私有字节[ ] content=new byte [ 1024 * 1024 ];
@Override
protectedvoidfinalize (
system.out.println(finalize运行);
}
}
publicstaticvoidmain (字符串[ ] args ) {
for(inti=0; I
Fddfdd=newFdd (;
}
}
}
`
现在做了班,设定参数吧。
` #最大堆内存
-Xmx5m
最小堆内存
-Xms5m
堆内存溢出错误打印
- xx : heapdumponoutofmemoryerror
将有关堆的信息保存到以下路径
- xx : heap dump path=f :/a.dump `
在main方法中,我们创建了1000个软驱对象。 如果不执行finalize方法,将进行垃圾回收,因为没有调用。 此时,制作几个都没有问题。 但是,如果finalize方法存在,则不是。
` Java.lang.out of memory error : javaheapspace
Dumping heap to F:/a.dump .
已完成
已完成
已完成
已完成
已完成
已完成
已完成
unabletocreatef :/a.dump :文件导出
exceptioninthread ' main ' Java.lang.out of memory error 3360 javaheapspace
atcom.FDD.chapter2. test finalizer $ FDD.(test finalizer.Java :6 )。
atcom.FDD.chapter2. test finalizer.main (test finalizer.Java :14 ) `
我们看到所有对象都运行finalize,在运行之前一直呆在堆里,运行结束后就被清理了,所以我们在这里运行了五次以上的finalize方法。 但是,如果对象超过了我们设置的5M,就会发生内存溢出。 一句话概括起来,出现了对象的堆积。 现在使用MAT工具分析一下吧。
Mat工具是插件,也可以下载自身。 下载结束后,打开我们刚生成的a.dump就可以了。
下图是分析的结果:
a内容属于Finalizer,也就是我们的软驱,b包含了比较多、剩下的乱七八糟的信息。 虽然也可以看到剩下的信息。 在MAT工具上。 有些准备与正在运行的finalizer运行。
确定,剩下的信息不会再显示了。
结论
GCRoot无法到达的对象不会立即回收到垃圾中,而是首先确定是否包含finalize方法,如果存在,则先运行finalize方法。 如果有很多这样的对象,GCRoot将无法立即到达,不再有用,留在内存中,并影响程序的效率。