如果Java finalize方法中存在无限循环或死锁,Finalizer线程将执行什么操作

如果Java finalize方法中存在无限循环或死锁Finalizer线程将执行什么操作。

规范写道:

在垃圾收集器回收对象的存储之前,Java虚拟机将调用该对象的终结器。

Java编程语言没有指定调用终结器的时间,除非说它将在重用对象的存储之前发生。

我读到这意味着终结者必须在存储可以重复使用之前完成。

Java编程语言没有指定哪个线程将为任何给定对象调用终结器。

重要的是要注意许多终结器线程可能是活动的(有时在大型共享内存多处理器上需要),并且如果大型连接数据结构变为垃圾,则可以调用该数据结构中每个对象的所有finalize方法同时,每个终结器调用在不同的线程中运行。

也就是说,最终化可能发生在垃圾收集器线程中,单独的thead中,甚至是单独的线程池中。

JVM不允许简单地中止执行终结器,并且只能使用有限数量的线程(线程是操作系统资源,操作系统不支持任意多个线程)。 因此,非终止终结器必然会使该线程池匮乏,从而禁止收集任何可终结的对象,并导致内存泄漏。

以下测试程序确认了此行为:

 public class Test { byte[] memoryHog = new byte[1024 * 1024]; @Override protected void finalize() throws Throwable { System.out.println("Finalizing " + this + " in thread " + Thread.currentThread()); for (;;); } public static void main(String[] args) { for (int i = 0; i < 1000; i++) { new Test(); } } } 

在Oracle JDK 7上,它打印:

 Finalizing tools.Test@1f1fba0 in thread Thread[Finalizer,8,system] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at tools.Test.(Test.java:5) at tools.Test.main(Test.java:15) 

我想说,既然Java规范没有说明必须如何调用finalize方法(只是必须在对象被垃圾收集之前调用它),行为是特定于实现的。

规范并不排除让多个线程运行该进程,但不需要它:

重要的是要注意许多终结器线程可能是活动的(有时在大型共享内存多处理器上需要),并且如果大型连接数据结构变为垃圾,则可以调用该数据结构中每个对象的所有finalize方法同时,每个终结器调用在不同的线程中运行。

查看JDK7的源代码, FinalizerThread会保留计划完成的对象队列(实际上,对象由GC添加到队列中,当certificate无法访问时 – 请检查ReferenceQueue doc):

 private static class FinalizerThread extends Thread { private volatile boolean running; FinalizerThread(ThreadGroup g) { super(g, "Finalizer"); } public void run() { if (running) return; running = true; for (;;) { try { Finalizer f = (Finalizer)queue.remove(); f.runFinalizer(); } catch (InterruptedException x) { continue; } } } } 

从队列中删除每个对象,并在其上运行runFinalizer方法。 如果对象运行了最终化,则进行检查,如果没有,则调用它作为对本机方法invokeFinalizeMethod的调用。 该方法只是在对象上调用finalize方法:

 JNIEXPORT void JNICALL Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz, jobject ob) { jclass cls; jmethodID mid; cls = (*env)->GetObjectClass(env, ob); if (cls == NULL) return; mid = (*env)->GetMethodID(env, cls, "finalize", "()V"); if (mid == NULL) return; (*env)->CallVoidMethod(env, ob, mid); } 

这应该导致一种情况,即对象在列表中排队,而FinalizerThread在故障对象上被阻塞,这反过来应该导致OutOfMemoryError

所以回答原来的问题:

如果Java finalize方法中存在无限循环或死锁,Finalizer线程将执行什么操作。

它只是坐在那里运行无限循环直到OutOfMemoryError

 public class FinalizeLoop { public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { for (;;) { new FinalizeLoop(); } } }; thread.setDaemon(true); thread.start(); while (true); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("Finalize called"); while (true); } } 

如果在JDK6和JDK7上只打印一次,请注意“Finalize called”。

对象将不会被“释放”,即不会从它们中回收内存,并且在finalize方法中释放的资源将始终保留。

基本上有一个队列包含所有对象等待执行的finalize()方法。 Finalizer线程从此队列中获取对象 – 运行finalize – 并释放该对象。

如果此线程将死锁,则ReferenceQueue Queue将会成长,并且在某些时候OOM错误将变得无法控制。 资源也将被此队列中的对象占用。 希望这可以帮助!!

 for(;;) { Finalizer f = java.lang.ref.Finalizer.ReferenceQueue.remove(); f.get().finalize(); }