Всем известно, что в java есть ссылки на SoftReference, WeakReference и PhantomReference. Все они наследуются от абстрактного класса Reference. Давайте посмотрим на его диаграмму классов:
Можно обнаружить, что, за исключением самой известной сильной ссылки, которая не имеет соответствующей реализации Reference, все виртуальные ссылки, слабые ссылки и мягкие ссылки имеют соответствующие классы реализации 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 и документация