Специальный справочник - FinalReference

Java

Всем известно, что в 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 и документация

woohoo.info Q.capability/article/JVM…