Слабая ссылка Java не освобождается после System.gc()

У меня есть простой фрагмент:

Integer integer = 2;
WeakReference<Integer> wi = new WeakReference<>(integer);
WeakReference<Integer> sr = new WeakReference<>(new Integer(3));
System.out.println(wi.get());
System.out.println(sr.get());
System.gc();
System.out.println("step 1 wi = " + wi.get());
System.out.println("step 1 sr =: " + sr.get());
integer = null;
System.gc();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("step 1 wi = " + wi.get());
System.out.println("step 1 sr =: " + sr.get());

Вызов «System.gc()» должен заставить все слабые ссылки быть переработанными, правильно, и я даже подождал 1 секунду, чтобы убедиться, что gc() происходит. Но даже когда установлено «integer = null», «wi» отказывается быть нулевым. В то время как sr имеет значение null сразу после "System.gc()". Он печатает:

2
3
step 1 wi = 2
step 1 sr =: null
step 1 wi = 2
step 1 sr =: null

Мои вопросы: (1) В чем основная разница между wi и sr? (2) как заставить jvm перерабатывать wi?

Большое спасибо.


person Hind Forsum    schedule 26.01.2019    source источник
comment
Вы должны подключиться к уведомлению gc: docs.oracle.com/javase/7/docs/jre/api/management/extension/com/, чтобы быть уверенным, что GC действительно выполнялся во время проверки этих значений.   -  person Krzysztof Cichocki    schedule 06.02.2019


Ответы (1)


Это не имеет никакого отношения ни к тому факту, что System.gc() не дает никаких гарантий (это не так, но это не то, почему вы видите то, что видите), ни к тому, как слабые ссылки взаимодействуют с gc.

Класс j.l.Integer содержит «кеш» экземпляров для Integer для всех значений байтов, то есть от -128 до +127. Вы можете посмотреть это в действии:

System.out.println(Integer.valueOf(127) == Integer.valueOf(127));
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));

Приведенное выше печатает true false.

Этот код:

Integer x = 2;

является синтаксическим сахаром. То, что компилятор на самом деле заканчивает компиляцией, потому что «автоупаковка» - это плод воображения javac (JVM ничего об этом не знает, это полностью javac делает это за вас), это следующий код:

Integer x = Integer.valueOf(2);

тогда как, конечно, если вы вызываете new Integer, вы ВСЕГДА получаете новый объект, потому что это то, что говорит спецификация. Вы также можете проверить это:

System.out.println(new Integer(5) == new Integer(5));

выше печатает false.

По сути, сам класс java.lang.Integer содержит ссылку на то, на что указывает ваша ссылка wi, поэтому он никогда не собирается.

Повторите тот же самый код, но на этот раз вместо '= 2' попробуйте '= 128', и на этот раз он соберет то же, что и sr.

person rzwitserloot    schedule 26.01.2019
comment
@KrzysztofCichocki Пожалуйста, объясните, может измениться в любое время. Все поведение, объясненное в этом ответе, хорошо задокументировано в Javadoc и спецификации языка Java. - person Sotirios Delimanolis; 27.01.2019
comment
@KrzysztofCichocki Вы ошибаетесь. Хотя я согласен с тем, что полагаться на целочисленный кеш кажется странным, этот ответ точен. Что касается WeakReference, то он не будет очищен, пока указанный объект строго доступен из кэша Integer. - person maaartinus; 29.01.2019
comment
Полагаться на внутренний кеш нехорошо, это всего лишь деталь реализации, которую можно изменить в любое время. - person Krzysztof Cichocki; 06.02.2019
comment
Не гарантируется, что System.gc() действительно запустит gc, он просто любезно просит jvm сделать это. Опять же, это просто деталь реализации, на которую вы не можете полагаться так точно, как и на JComponent.repaint() и Thread.yeld(), хотя эти вызовы обычно имеют некоторый эффект, вы не можете гарантировать, что они действительно будут запустить базовую задачу в определенное время. - person Krzysztof Cichocki; 06.02.2019
comment
Вы должны подключиться к уведомлению gc: docs.oracle.com/javase/7/docs/jre/api/management/extension/com/, чтобы быть уверенным, что GC действительно выполнялся во время проверки этих значений. - person Krzysztof Cichocki; 06.02.2019