FinalReference Quote
This class is a package type, indicating that it is not a public part and inherits from Reference, which means that it is also a specific reference type. Therefore, before each object wrapped in it is recycled, it will be placed in the specified referqyebceQueue.
This reference object is specifically used for class with finalize methods. It can be understood that every object with corresponding methods will be encapsulated as a finalRefernece object.
Because the finalize method is defined by object, its default implementation is empty. Then if this method is rewritten, the method body will definitely not be empty. This difference can be made through this. As long as the finalize method implements a class that is not empty, the generated objects need to be registered in finalRefernece.
This step can be registered accordingly by calling the object default constructor method during newInstance.
Finalizer#register method
When this method is called mainly, the corresponding finalizer object will be generated, and the finalizer object is inherited from finalReference. This method is declared as follows:
/* Invoked by VM */static void register(Object finalizee) { new Finalizer(finalizee);} As can be seen from the above comments, this method will be called by jvm at a specific period.
Then switch to Finalizer's constructor, as shown below:
private Finalizer(Object finalizee) { super(finalizee, queue); add();} It can be seen that the corresponding reference object will be called back through queue. The function of add is to save all objects that have not yet finalize the method and call it at the end System.shutdown . Set it through Runtime#runFinalizersOnExit .
ReferenceQueue
This reference queue will be placed in this queue before the internal objects of the corresponding reference object are recycled (the detailed description will be explained in another article about reference.), because only the corresponding object needs to be obtained from this queue, then this object is definitely ready to be recycled.
Then call the corresponding finalize method before recycling.
FinalizerThread thread
This thread keeps getting data from the queue, and then calls the corresponding finalize method. The corresponding code is as follows:
for (;;) { try { Finalizer f = (Finalizer)queue.remove(); f.runFinalizer(jla); } catch (InterruptedException x) { // ignore and continue }}The corresponding runFinalizer is as follows:
synchronized (this) { if (hasBeenFinalized()) return; remove();}try { Object finalize = 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();In the above logic, first call remove and remove it from the finalize. This method is to ensure that the finalize of each object will only be called once at most, that is, the current call is completed. It will be recorded in the corresponding state, that is, hasBeenFinalized returns true (in fact, it means pointing the next pointer in it to itself. That is, it has never been finalize, and there is no need to call finalize again).
Next is to call the corresponding finalize method. jla.invokeFinalize above is actually to call the finalize method of the corresponding object. In this process, the original object is first obtained through get. In the entire jvm process, the reference is not set to null before recycling for finalizeReference. Because here, the corresponding reference object can always be obtained.
After processing, the corresponding clear is finally called to clear the corresponding reference. This achieves the effect of the final reference having no other object to reference.
In the above processing, there is no limit on the time to call finalize. Therefore, once the finalize of an object is called slowly, it will affect the execution of the entire recycling chain, and a corresponding OOM exception will be generated. Therefore, unless in special cases, do not rewrite finalize. There should be other methods to handle the corresponding scenarios. For example, FinalizableReference in guava.
Finalizer starts the thread
In the above thread, it will be started during the corresponding process startup. It can be understood that the object triggers the initialization of the finalizer class by calling register(object) . Then, in the static initialization block, the corresponding recycling thread will be started. The corresponding initialization code is as follows:
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();}The static above is a static initialization block, that is, as long as the class Finalizer is used, the corresponding call will be triggered. The thread group used here is a system thread group, and the priority is still relatively high, and it is configured as a background thread.
When using jstack to print threads, the threads shown below the figure appear are started here. As shown in the figure below
Summarize
The entire Finalizer works together through finalReference by the JVM and the corresponding java classes. It is not all implemented by jvm, so it can be considered that it is not too underlying, but to implement the corresponding semantics. Everything is done by normal java and cooperated by jvm. Understanding the entire process is also an understanding of the running mechanism of java itself.