如果在Java的finalize方法中出现无限循环或死锁情况,Finalizer线程会怎么处理?
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();
}
}
}
Finalizing tools.Test@1f1fba0 in thread Thread[Finalizer,8,system]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at tools.Test.<init>(Test.java:5)
at tools.Test.main(Test.java:15)
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);
}
}
这些对象将不会被“释放”,也就是说它们的内存不会被回收,同时在 finalize 方法中释放的资源将会一直保留。
基本上,有一个队列保存着所有等待执行 finalize() 方法的对象。Finalizer 线程从该队列中取出对象,运行 finalize,然后释放该对象。
如果此线程出现死锁,则 ReferenceQueue 队列将增长,并且在某个时刻 OOM 错误将变得无法避免。此外,该队列中的对象会占用资源。希望这可以帮助你!
for(;;)
{
Finalizer f = java.lang.ref.Finalizer.ReferenceQueue.remove();
f.get().finalize();
}
finalize
方法会被调用,更不用说你不知道它何时会被调用。 - morganoprintln
就足够了。我很久以前就玩过这个,finalize
很快就会被调用。只有在系统关闭时才会有一些无法访问的对象没有被终结的情况。 - Marko Topolnikfinalize
方法将会被调用:"可以针对一个对象调用的 finalize 的特殊定义称为该对象的 finalizer。在垃圾回收器回收对象存储之前,Java虚拟机将会调用该对象的 finalizer。" - lpiepiora