Я решил продолжить https://stackoverflow.com/a/41998907/2674303 в отдельной теме.
Рассмотрим следующий пример:
public class SimpleGCExample {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> queue=new ReferenceQueue<>();
SimpleGCExample e = new SimpleGCExample();
Reference<Object> pRef=new PhantomReference<>(e, queue),
wRef=new WeakReference<>(e, queue);
e = null;
for(int count=0, collected=0; collected<2; ) {
Reference ref=queue.remove(100);
if(ref==null) {
System.gc();
count++;
}
else {
collected++;
System.out.println((ref==wRef? "weak": "phantom")
+" reference enqueued after "+count+" gc polls");
}
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("finalizing the object in "+Thread.currentThread());
Thread.sleep(100);
System.out.println("done finalizing.");
}
}
Java 11 печатает следующее:
finalizing the object in Thread[Finalizer,8,system]
weak reference enqueued after 1 gc polls
done finalizing.
phantom reference enqueued after 3 gc polls
Первые 2 строки могут изменить порядок. Похоже, они работают параллельно.
Последний ряд иногда печатает 2 опроса gc, а иногда 3
Таким образом, я вижу, что включение PhantomReference занимает больше циклов GC. Как это объяснить? Это упомянуто где-то в документации (я не могу найти)?
приписка
Слабая ссылка Java документ:
Предположим, что сборщик мусора определяет в определенный момент
время, когда объект слабо достижим. В то время это будет
атомарно очистить все слабые ссылки на этот объект и все слабые
ссылки на любые другие слабо достижимые объекты, из которых это
объект доступен через цепочку сильных и мягких ссылок. В
в то же время он объявит все ранее слабодоступные
объекты должны быть завершены. В то же время или в более позднее время
поставит в очередь те недавно очищенные слабые ссылки, которые зарегистрированы
с эталонными очередями
PhantomReference java doc:
Предположим, сборщик мусора определяет в определенный момент времени
что объект фантомно достижим. В это время это будет атомно
очистить все фантомные ссылки на этот объект и все фантомные ссылки
к любым другим фантомно достижимым объектам, из которых этот объект
достижимы. В то же время или в более позднее время
те недавно очищенные фантомные ссылки, которые зарегистрированы
справочные очереди
Разница мне не ясна
P.S. (Речь идет об объекте с нетривиальным методом финализации)
Я получил ответ на свой вопрос от @Holger:
Он (никакого сексизма, но я полагаю, что так) указал мне на java doc и заметил, что PhantomReference содержит дополнительную фразу по сравнению с «Мягкими и слабыми ссылками»:
Объект слабо доступен, если он не является ни сильно, ни мягко
достижимо, но может быть достигнуто путем обхода слабой ссылки. Когда
слабые ссылки на слабодоступный объект очищаются, объект
получает право на финализацию.
Объект фантомно достижим, если
это ни сильно, ни мягко, ни слабо достижимо,
доработано , и какая-то фантомная ссылка ссылается на него
Мой следующий вопрос был о том, что это значит он был завершен Я ожидал, что это означает, что метод finalize был завершен
Чтобы доказать это, я изменил приложение так:
public class SimpleGCExample {
static SimpleGCExample object;
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
SimpleGCExample e = new SimpleGCExample();
Reference<Object> pRef = new PhantomReference<>(e, queue),
wRef = new WeakReference<>(e, queue);
e = null;
for (int count = 0, collected = 0; collected < 2; ) {
Reference ref = queue.remove(100);
if (ref == null) {
System.gc();
count++;
} else {
collected++;
System.out.println((ref == wRef ? "weak" : "phantom")
+ " reference enqueued after " + count + " gc polls");
}
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("finalizing the object in " + Thread.currentThread());
Thread.sleep(10000);
System.out.println("done finalizing.");
object = this;
}
}
Я вижу следующий вывод:
weak reference enqueued after 1 gc polls
finalizing the object in Thread[Finalizer,8,system]
done finalizing.
И приложение зависает. Я думаю, это потому, что для слабых / мягких ссылок GC работает следующим образом: как только GC обнаруживает, что объект является слабым / мягким достижимым, он выполняет 2 действия параллельно :
- включить Weak / Soft в зарегистрированный экземпляр ReferenceQueue
- Выполнить метод финализации
Так что для добавления в ReferenceQueue не имеет значения, воскрес объект или нет.
Но для PhantomReference действия разные. Как только GC обнаружил, что объект является фантомно доступным, он последовательно выполняет следующие действия:
- Выполнить метод финализации
- Проверьте, что объект все еще только phantomReachable (убедитесь, что объект не был воскрешен во время выполнения метода finalize). И только если объект GC добавляет фантомную ссылку в ReferenceQueue
Но @Holger сказал, что оно было завершено означает, что JVM инициировала finalize () вызов метода и для добавления PhantomReference в ReferenceQueue не имеет значения, завершилось оно или нет. Но, похоже, мой пример показывает, что это действительно важно.
Честно говоря, я не понимаю разницы в соответствии с добавлением в RefernceQueue для слабой и мягкой ссылки.В чем была идея?