#java #finalizer
#java #финализатор
Вопрос:
Мне трудно понять последствия разделов 12.6.1 и 12.6.2 спецификации языка Java SE 8. Я работаю с продуктом, в котором объекты Java управляют собственными одноранговыми узлами, поэтому важно правильно завершить доработку (до тех пор, пока у нас не появится возможность переписать ref-очередь).
Из спецификации ясно, что финализаторы могут быть запущены не по порядку. Это достижимость, с которой мне приходится нелегко.
Я считаю, что в спецификации сказано следующее:
class A {
public Object o = new Object()
protected synchronized void finalize() throws Throwable { o = null; }
}
class B {
A a = new A()
protected void finalize() throws Throwable {
a.getClass() // always works: a cannot be null.
a.o.getClass() // might NPE: a's finalizer might have been run
}
}
class C {
A a = new A()
protected void finalize() throws Throwable {
synchronized (a) {
a.getClass() // always works: a cannot be null.
a.o.getClass() // always works: a.o cannot be null.
}
}
}
Выше приведено 4 утверждения. Я был бы очень признателен за подтверждение того, что они верны, или за объяснения того, почему один или несколько из них являются ложными.
Комментарии:
1. получение правильного завершения Будьте предельно осторожны, поскольку завершение по существу должно использоваться только как отказоустойчивое средство в случае, если объекты не были правильно удалены. Как вы уже заметили, гарантий вокруг этого мало. Большинство библиотек, выполняющих то, о чем вы говорите (например, SWT), явно требуют
dispose
метода или аналогичного, и Java try-with-resources сделала это намного проще в обращении.
Ответ №1:
Если A.finalize
сначала получит блокировку, то a.o.getClass()
будет NPE даже внутри C
. Окончательное утверждение неверно.
С точки зрения достижимости подумайте об этом так: предположим A
, и C
были циклически достижимы, тогда они достигли бы недостижимости в «одно и то же время». Таким образом, не может быть разумно, что A
это должно быть завершено раньше C
.
Я предполагаю, что прямым исправлением может быть подсчет ссылок A
. Однако полагаться на GC для очистки ресурсов, не связанных с памятью, будет не очень хорошо.
Комментарии:
1. Правильно. Тупой пример. C может не перехватывать блокировку a до тех пор, пока a не будет завершено. Feh. Считаете ли вы, что остальные 3 утверждения верны? Я не слежу за вашим обсуждением достижимости: дело в том, что A может быть завершено до B или C.
2. @G.BlakeMeike Да.
A
могут быть доработаны доB
илиC
даже несмотря на то, что они до ссылкиA
. Это было бы верно , даже еслиA
бы у него было полеpublic C c;
иC
был конструкторC() { a.c = this; }
.3. ДА. Проблема с конструктором кажется неуместной, если я вас правильно понимаю. Что кажется важным, так это то, что, независимо от порядка завершения, ни B, ни C никогда не видят поле ‘a’ как нулевое. Возможно, экземпляр A был завершен, но ссылка никогда не бывает нулевой.
4. Я надеялся на редактирование, которое давало бы прямые ответы на этот вопрос. Тем не менее, ответы на эти вопросы присутствуют… если похоронен в комментариях.
5. @G.Блейкмейк Да. Я думаю, что это было между последним использованием и завершением, о котором я думал.