Доработка полей объекта

#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.Блейкмейк Да. Я думаю, что это было между последним использованием и завершением, о котором я думал.