Всем известно, что в java есть ссылки на SoftReference, WeakReference и PhantomReference. Все они наследуются от абстрактного класса Reference. Давайте посмотрим на его диаграмму классов:
Итак, что же делает дополнительная реализация FinalReference?
FinalReference
Как видите, класс FinalReference просто наследует класс Reference.
/**
* Final references, used to implement finalization
*/
class FinalReference<T> extends Reference<T> {
public FinalReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
В комментариях говорят, что он используется для достижения финализации (финализации).
Его настоящая логика находится в единственном подклассе FinalReference:java.lang.ref.Finalizerсередина.
Обратите внимание, что этот класс является закрытым на уровне пакета, модифицированным с помощью ключевого слова final, а конструктор является закрытым, предоставляя метод регистрации для вызова JVM.
Конструктор и метод регистрации
final class Finalizer extends FinalReference<Object> {
//...
private Finalizer(Object finalizee) {
super(finalizee, queue);
add();
}
/* Invoked by VM */
static void register(Object finalizee) {
new Finalizer(finalizee);
}
}
Поскольку метод register не возвращает значение, созданный объект Finalizer нельзя получить извне.
Среди них вызов в конструктореsuper(finalizee, queue)Финализатор будет добавлен в очередь ссылокqueueсередина.
Справочные очереди см.nuggets.capable/post/684490…
Метод add, вызываемый в конструкторе передачи, который мы проанализировали.
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
private static Finalizer unfinalized = null;
private static final Object lock = new Object();
private Finalizer
next = null,
prev = null;
//...
private void add() {
synchronized (lock) {
if (unfinalized != null) {
this.next = unfinalized;
unfinalized.prev = this;
}
unfinalized = this;
}
}
комбинироватьnext,prevсвойства иaddметод, легче увидетьunfinalizedна самом деле является двусвязным списком, вaddПосле вызова метода текущий объект добавляется вunfinalizedсвязанный список.
Фактически, в构造方法После вызова метода он делает две вещи:
- Вызовите super, чтобы зарегистрировать входной объект в очереди ссылок.
- Вызовите метод add, чтобы добавить созданный в данный момент объект
unfinalizedсвязанный список.
потому чтоregisterметод не возвращает значение, иunfinalizedСвойство является статической переменной-членом, поэтому текущий созданный объект является толькоunfinalizedСвязанный список содержит ссылку
Согласно аннотациям и правилам доступа,registerМетод будет вызываться только виртуальной машиной и только переопределяться.java.lang.Object#finalizeКласс метода будет вызываться как параметрFinalizer#registerметод.
фоновая нить
Подобно ожидающим обработчикам, статические блоки кода также используются в FinalReference для запуска фоновых потоков.
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
посмотриFinalizerThreadкласс, который наследует класс Thread и переопределяетrunметод.
private static class FinalizerThread extends Thread {
private volatile boolean running;
public void run() {
// in case of recursive call to run()
if (running)
return;
//...
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and continue
}
}
}
}
В приведенном выше фрагменте кода хранится только ключевой код процесса.
Видно, что в методе run используется бесконечный цикл, в каждом цикле из эталонной очереди вынимается первый элемент очереди (в конструкторе объект регистрируется в эталонной очереди, и когда эталонный статус становится pending, Pending-handler-thread добавляет его в очередь регистрации) и выполняетrunFinalizerметод.
Продолжай читатьrunFinalizerметод:
private void runFinalizer(JavaLangAccess jla) {
synchronized (this) {
if (hasBeenFinalized()) return;
remove();
}
try {
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(finalizee);
/* Clear stack slot containing this variable, to decrease
the chances of false retention with a conservative GC */
finalizee = null;
}
} catch (Throwable x) { }
super.clear();
}
По названию метода видно, что функция метода заключается в выполненииjla.invokeFinalize(finalizee)и выполните некоторую очистку.
пока вJavaLangAccessВ классе реализации (анонимный внутренний класс в java.lang.System)invokeFinalizeКод также очень прост, просто вызовитеfinalizeметод.
public void invokeFinalize(Object o) throws Throwable {
o.finalize();
}
существуетinvokeFinalizeПосле этого код активно устанавливает finalizee в null, согласно приведенным выше комментариям, чтобы очистить ссылку метода на текущий объект и уменьшить вероятность воздействия на gc.
При выполнении метода finalizee к объекту будет временно добавлена сильная ссылка, что повлияет на сборщик мусора.
завершить метод
Из приведенного выше процесса анализа видно, чтоjava.lang.Objectсерединаfinalizeметоды вызываются потоком демона, когда объект собирается быть собранным.finalizeметод.
Поскольку приоритет потока не гарантируется, в вызове preparefinalizeК моменту вызова метода он может пройти несколько gcs, и из-за временной строгой ссылки объект долгое время не восстанавливался.
но,finalizeзвонки не гарантируются. Таким образом, этот метод был помечен как устаревший в java9. Мы также не должны переопределять этот метод для очистки.
Суммировать
По сути, FinalReference — это класс, созданный jdk для реализации метода Finalizer, аналогичного методу деструктора.
Виртуальная машина сначала регистрирует объект, переписавший метод Finalizer, в очередь ссылок и временно сохраняет его в связанном списке.
Когда состояние ссылки на объект становится Enqueued, поток демона извлекает объект из очереди ссылок, устанавливает временную сильную ссылку и вызывает метод Finalizer.
Из-за низкого приоритета потока демона нет гарантии, что переопределенный метод Finalizer будет выполнен до повторного использования. А из-за существования временных сильных ссылок объект также может пропустить gc.
Поэтому метод Finalizer не следует использовать~
использованная литература
исходный код jdk8 и документация