finalize方法
采访中通常会问一个Java问题:“ finalize()
方法的目的是什么?” 尽管您可以回答finalize()
方法的通常目的是在对象被丢弃之前执行清理操作,但它还有很多其他功能。 在幕后,需要以特殊方式处理finalize()
方法。 否则, finalize()
方法中的一个小错误有可能危害整个应用程序的可用性。 让我们仔细看看。
幕后花絮
尽管这看起来很基础,但是具有finalize()
方法的对象在垃圾回收过程中的处理方式与没有垃圾方法的对象不同。 在垃圾回收阶段,带有finalize()
方法的对象不会立即从内存中清除。 而是将这些对象添加到java.lang.ref.Finalizer
的内部队列中。
在整个JVM中,只有一个名称为'Finalizer'的低优先级JVM线程,它执行队列中每个对象的finalize()
方法。 仅在执行finalize()
方法之后,该对象才有资格进行垃圾回收。 如果应用程序正在生成很多具有finalize()
方法的对象,则低优先级的“ Finalizer”线程无法跟上执行finalize()
方法的步伐。 然后,大量未完成的对象将开始在java.lang.ref.Finalizer
的内部队列中建立,从而导致大量的内存浪费。
有时,由于不良的编程习惯,“ finalize()
”线程可能在执行finalize()
方法时开始等待或阻塞。 如果“ Finalizer”线程开始等待或阻塞,则java.lang.ref.Finalizer
内部队列中未完成对象的数量将开始显着增长。 最终会导致OutOfMemoryError
,从而危害JVM的可用性。
例
为了说明这一理论,我们编写了一个简单的示例程序。
public class SampleObject {
public String data;
public SampleObject(String data) {
this.data = data;
}
@Override
public void finalize() {
try {
// Sleep for 1 minute.
Thread.currentThread().sleep(1 * 60 * 1000);
} catch (Exception e) {}
}
public static void main(String[] args) {
long counter = 0;
while (true) {
new SampleObject("my-fun-data-" + counter);
System.out.println("created: " + counter++);
}
}
}
基本上,此类的main()
方法连续创建SampleObject
。 有趣的是,该程序的一部分是finalize()
方法。 该方法使当前正在执行的线程(即“ Finalizer”线程)进入睡眠状态1分钟。 此示例说明了finalize()
方法执行不佳期间会发生什么。
当我们以最大10 mb(即-Xmx10M
)的最大堆大小运行上述程序时,它在启动后几秒崩溃,并显示java.lang.OutOfMemoryError
错误消息。
该程序崩溃是因为可以在执行finalize()
方法之后从内存中逐出SampleObject
。 由于“终结器”线程处于Hibernate状态,因此它无法以main()
方法创建新SampleObject
的相同速率执行finalize()
方法。 因此,内存已满,程序导致了java.lang.OutOfMemoryError
错误消息。
另一方面,当我们注释掉finalize()
方法时,该程序连续运行,而没有遇到任何java.lang.OutOfMemoryError
错误。
还请参见:
如何诊断这个问题?
您的应用程序可能包含数百,数千甚至数百万个类。 它包括来自第三方库和框架的类。 因此,您将如何识别实现不佳的finalize()
方法? 这是诸如HeapHero.io之类的堆转储分析工具很方便的地方。
从上面的程序中捕获堆转储并将其上载到HeapHero.io后,它会生成此有用的报告 。 HeapHero.io报告包含多个部分,但我们感兴趣的部分是“等待完成的对象”。
报告的此部分显示由于对象等待应用程序中的完成而浪费的内存量。 在此假设的示例中,内存浪费为7.66 MB(97.2%)。
单击“等待完成的对象有哪些?”下给出的超链接,您将能够看到等待完成的对象。 基本上,您将能够看到jlrReferenceQueue
的对象树(注意:这是java.lang.ref.Finalizer
对象的队列,其中包含需要执行finalize()
方法的所有对象的引用)。 如果您沿着树走下去,它将显示队列中等待完成的对象。 在这里,您可以看到队列中的两种对象:
-
com.petals.finalize.SampleObject.data
–占用内存的56.8% -
com.petals.finalize.SampleObject
–占内存的11.5%
答对了!! 这些是在示例程序中创建的对象。 因此,利用类似HeapHero的工具可以很容易地看出finalize()
方法在哪里实现不佳。
翻译自: https://jaxenter.com/happens-behind-scenes-finalize-method-144975.html
finalize方法