Статьи

Java: бессмертные объекты и воскресение объектов

Что такое воскресение объекта?

Java-объект имеет право на сборку мусора, если никакой другой объект не ссылается на объект. Когда сборщик мусора JVM в конечном итоге собирается удалить неиспользуемый объект, вызывается метод finalize() объекта. Но если мы снова создадим ссылку на объект в собственном методе finalize() объекта, объект можно воскресить. В таких случаях JVM обнаружит, что на объект снова ссылаются, и воздержится от его удаления. Образно говоря, объект воскрес из смерти …

01
02
03
04
05
06
07
08
09
10
11
public class Immortal {
 
    private static final Set<Immortal> immortals = new HashSet<>();
 
    @Override
    protected void finalize() throws Throwable {
        System.out.println(Immortal.class.getSimpleName() + "::finalize for " + this);
        immortals.add(this); // Resurrect the object by creating a new reference
    }
 
}

Свойство воскрешения можно проверить следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class NewMain {
 
    public static void main(String[] args) {
        new Immortal();
        System.gc();
        sleep(1_000);
        System.gc();
        prompt("Press any key...");
    }
 
    private static void prompt(String msg) {
        try {
            System.out.println(msg);
            System.in.read();
        } catch (IOException io) {
        }
    }
 
    private static void sleep(long duration) {
        try {
            Thread.sleep(duration);
        } catch (InterruptedException ie) {
        }
    }
 
}

Который даст следующий вывод:

1
2
Immortal::finalize for com.blogspot.minborgsjavapot.resurected_object.Immortal@635cb856
Press any key...

Изучив кучу Java, мы также видим, что объект все еще там, несмотря на то, что его финализатор был вызван:

1
2
3
4
5
6
pemi$ jps
21735 NewMain
21736 Jps
 
pemi$ jmap -histo 21735 | grep Immortal
 164:             1             16  com.blogspot.minborgsjavapot.resurected_object.Immortal

Сколько раз вызывается финализатор?

Если на воскресший объект позднее дается ссылка, он снова имеет право на сборку мусора. Однако на этот раз
Метод finalize() больше не будет вызываться, поскольку Java вызывает финализатор не более одного раза. Как мы помним, нет никакой гарантии, что финализатор когда-либо будет вызван. Например, если программа по какой-либо причине завершает работу, объекты в JVM просто прекращаются, и их финализаторы вообще не будут вызываться, как это видно в этом примере:

1
2
3
4
5
6
7
public class NewMain2 {
 
    public static void main(String[] args) {
        new Immortal();
    }
 
}

Когда мы запускаем приведенный выше фрагмент кода, мы видим, что Immortal::finalizer никогда не вызывается.

Воскресение объекта хорошо?

Как всегда при использовании метода finalize() , мы должны быть очень осторожны. Общая рекомендация для нас, разработчиков Java, — вообще не использовать finalize() . Кроме того, можно утверждать, что воскрешение объекта — это то же самое, что намеренное создание утечки памяти.

Однако есть несколько интересных приложений для воскрешения объекта. Возможно, мы хотим провести некоторый посмертный анализ наших объектов без изменения фактического приложения, использующего объекты. Используя воскресение объектов, мы можем сохранить эти объекты и проанализировать их внутреннее состояние позже, независимо от приложений, которые их используют.