каким будет Finalizer нить делать, если есть бесконечный цикл, или тупиковой в Java завершить метод., что будет нить Финалайзер делать, если есть бесконечный цикл или тупиковый в Java завершить метод
, что будет нить Финалайзер делать, если есть бесконечный цикл или тупиковый в Java завершить метод
ответ
Спецификация пишет:
Перед тем как хранилище для объекта будет утилизирован сборщиком мусора, то виртуальная машина Java будет вызывать финализации этого объекта.
Язык программирования Java не указывает, как скоро будет вызываться финализатор, кроме как сказать, что это произойдет до того, как хранилище будет повторно использовано для объекта.
Я прочитал это, чтобы завершить финализатор до того, как хранилище может быть повторно использовано.
Язык программирования Java не указывает, какой поток будет вызывать финализатор для любого заданного объекта.
Важно отметить, что многие потоки финализаторов могут быть активными (это иногда необходимо для больших многопроцессорных систем с общей памятью), и если большая связанная структура данных становится мусором, все методы завершения для каждого объекта в этом структура данных может быть вызвана одновременно, причем каждый вызов финализатора работает в другом потоке.
То есть финализация может возникать в резьбе коллектора мусора, в отдельном аэде или даже в отдельном пуле потоков.
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 [email protected] 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)
+1, сделал более или менее тот же тест и получил тот же результат. Включается только один поток финализатора. –
@mriton, что означает значение ** _ Важно отметить, что многие потоки финализаторов могут быть активными (это иногда необходимо для больших многопроцессорных систем с общей памятью) _ **. Имеет ли она связь с алгоритмом GC, который мы выбрали, например CMS. И существует ли связь между количеством потоков Finalizer и алгоритмом GC. – andy
JVM может сделать это, но он хочет, и разные JVM могут делать это по-другому. Если вас интересует конкретная JVM, проверьте ее документацию (или, скорее, ее исходный код). Похоже, что анонс lpiepiora указывает на то, что в JVM Oracle всегда есть один поток финализатора, независимо от используемого алгоритма сбора мусора. – meriton
Я бы сказал, что, так как спецификации Java не говорит как должен быть вызван метод finalize (просто чтобы он был вызван, прежде чем объект будет собран мусором), поведение является специфичным для реализации.
Спецификация не исключает, имеющими несколько потоков выполнения процесса, но не требует этого:
Важно отметить, что многие финализатор нить может быть активной (иногда это необходимо на большие многопроцессорные системы с общей памятью) и , что если большая связанная структура данных становится мусором, все методы окончательной обработки для каждого объекта в этой структуре данных могут быть вызваны в одно и то же время , причем каждый вызов финализатора работает в различных потоках.
Глядя на источниках JDK7, то FinalizerThread
держит очереди объектов, запланированных для завершения (на самом деле объекты добавляются в очередь с помощью GC, когда оказалось недостижимым - проверить ReferenceQueue
документ):
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
. Проверка выполняется, если завершение было выполнено на объекте, а если оно не вызывается, в качестве вызова метода native 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 завершить метод.
Он будет просто сидеть и запускать этот бесконечный цикл до 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.
извините, я не согласен с вашим примером кода выше. цикл for будет завершаться одновременно с основным потоком, поэтому вы ** не можете ** видеть что-либо. – andy
@ и вы правы, я немного изменил код, чтобы быть уверенным, что вы что-то увидите в конце концов - я думаю, в зависимости от того, когда вы входите в GC. Спасибо! – lpiepiora
Объекты не будут «освобождены», то есть память не будет возвращена из них, а также ресурсы, освобожденные в методе finalize, будут сохраняться на всем протяжении.
В основном существует очередь, в которой хранятся все объекты, ожидающие выполнения их метода finalize(). Финализатор потока захватывает объекты из этой очереди - запускает финализацию - и освобождает объект.
Если этот поток будет заторможен, очередь ReferenceQueue будет расти, и в какой-то момент ошибка OOM станет неумолимой. Также ресурсы будут забиты объектами в этой очереди. Надеюсь это поможет!!
for(;;)
{
Finalizer f = java.lang.ref.Finalizer.ReferenceQueue.remove();
f.get().finalize();
}
Можете ли вы поделиться с нами каким-либо поведением, которое вы наблюдали при этом? –
Это трудно заметить, JVM даже не гарантирует, что 'finalize' будет вызван, иголки, чтобы сказать, что вы не знаете, когда он будет называться – morgano
@morgano Простой' println' достаточно. Я играл с этим давно, 'finalize' получает вызов довольно быстро. Есть только редкие случаи, когда некоторые недоступные объекты не дорабатываются до выключения системы. –