Java finalize function and soft reference, weak reference and virtual reference

1, Role of finalize function     &nb...
1. Object recycling that overrides the finalize method
1, Role of finalize function

        It is not a destructor in C/C + +, but a compromise made by java to make it easier for C/C + + programmers to accept it. In other words, the finalize function was originally designed to be a destructor similar to C/C + +, which is used for the final memory recovery before the object is destroyed. The similarities and differences between Java and C/C + + lie in: in C + +, the time when the memory of the object is recycled can be clearly determined (assuming that the program has no defects). Once the object of C + + is to be recycled, the destructor of the object will be called before recycling the object, and the memory occupied by the object will be released in the function; In Java, when the object's memory is recycled depends on when the garbage collector runs. Once the garbage collector is ready to release the storage space occupied by the object, it will first call its finalize() method, and the memory occupied by the object will be recycled only when the next garbage collection action occurs. Because the JVM garbage collection running time is uncertain, Therefore, the call to finalize () is uncertain. The JVM only guarantees that the method will be called, but does not guarantee that the tasks in the method will be executed. (this can be learned from the Java source code Finalizer.class: in the source code, the method of finalizing () is executed by starting a low priority thread, while finalizing () Any exception during the execution of the method will be caught and ignored, so there is no guarantee that the tasks in the finalize method will be executed). Because the thread executing finalize() is a low priority thread, since it is a new thread, although the priority is a little low, it is also executed concurrently with the garbage collector, so the garbage collector does not need to wait for the low priority thread to finish executing. In other words, it is possible that the low priority thread will execute the finalize () method only after the object is recycled.

        In other words, in C + +, we rely on destructors to release resources, but we can't expect the finalize() method to release resources. Therefore, the suggestion is not to expect the finalize function to release resources, but to actively and explicitly release relevant resources in the code. However, although this function is not recommended, it does not prevent us from understanding when it is called during JVM garbage collection. We know that the finalize() method is a method with empty method body in the Object class, and all classes we create inherit the Object class by default. Therefore, the JVM will call the finalize function only when we override the method in the custom class and reclaim the class we defined. Only when the JVM needs to call the finalize function, It only needs to perform garbage collection twice to destroy the class we defined. That is, if the method is not overridden in the class we define, garbage collection only needs to be executed once. What is the specific mechanism of garbage collection?

        When the class we define overrides the finalize method, during the initialization of the class, the object of this class will be wrapped into a java.lang.ref.Finalizer and added to the static linked list unfinalized of the Finalizer class. When the first garbage collection is performed, it is found that the object has a finalize method and has not been executed. Therefore, the object will not be recycled, but will be removed from the unfinalized linked list and added to the static reference queue of the Finalizer class. Looking at the Finalizer source code, you can see an internal class: java.lang.ref.Finalizer.FinalizerThread, which is used to monitor the reference queue of the Finalizer. When it finds a change in the queue, it removes the objects in the queue in turn and calls the finalize() function of the object. When the second garbage collection is performed, it is found that the class has overridden the finalize method but has been executed. You can directly recycle the class. The above is the recycling process of the class that overrides the finalize function. For classes that have not overridden the finalize function or have executed the finalize function once, it is easier to recycle the garbage directly. The reason why the finalize function can only be executed once here is to prevent the class from reviving the class when executing the finalize function, so that the class can never be recycled.

2, Soft reference, weak reference, virtual reference

        The meaning of these three references will not be introduced here. If necessary, you can baidu by yourself. Here we focus on the different purposes of designing these three references.

          Soft reference: soft reference is used to describe non essential objects. Soft reference is often used to implement memory sensitive cache. The reason is that the garbage collector will not easily recycle objects with soft references. Only when memory is insufficient, the garbage collection thread will recycle soft reference objects.

        Weak reference: weak reference is also used to describe non essential objects, but its strength is weaker than soft reference. The objects associated with weak reference can only survive until the next garbage collection. When the garbage collector works, objects associated with only weak references will be recycled regardless of whether the current memory is sufficient or not.

        Virtual reference: virtual reference is also called ghost reference or phantom reference. It is the weakest reference relationship. Whether an object has a virtual reference will not affect its lifetime, nor can it be used to obtain an object instance through virtual reference. The only purpose of setting virtual reference Association for an object is to receive a system notification after the object is recycled by the garbage collector.

        Reference queue: when creating soft reference, weak reference or virtual reference of an object, you can associate the reference object with a reference queue. When the garbage collector decides to garbage collect these objects, it will add the reference object to the associated reference queue. By opening the thread to listen to the changes of the reference queue, you can take corresponding actions when the object is recycled. Since the only purpose of a virtual reference is to receive the system notification after the object is collected by the garbage collector, a reference queue must be associated when creating a virtual reference, while soft references and weak references are not necessary. The so-called receiving system notification here is actually realized by opening the thread to listen to the changes of the reference queue. It should also be emphasized here that for soft references and weak references, when garbage collection is performed for the first time, the soft reference or weak reference object will be added to its associated reference queue, and then its finalize function will be executed (if it is not overwritten, it will not be executed); For virtual references, if the referenced object does not override the finalize method, the virtual reference object will be added to the reference queue only after the first garbage collection destroys the class. If the referenced object overrides the finalize method, the virtual reference object will be added to its associated reference queue only after the second garbage collection. This is proved by experiments.

1. Object recycling that overrides the finalize method

The following code tests the change process of virtual reference and weak reference queue when the TestClass object overriding the finalize method is garbage collected. The reason why the sleep statement is executed in the code is to ensure that the garbage collection is executed. If the sleep statement is not executed, the expected results cannot be obtained.

import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class TestPhantomReference { public static void main(String[] args) { ReferenceQueue phantomRQ = new ReferenceQueue();//Virtual reference queue ReferenceQueue weakRQ = new ReferenceQueue();//Weak reference queue TestClass testClass = new TestClass();//Recycled object PhantomReference pr = new PhantomReference(testClass, phantomRQ);//Virtual reference WeakReference wr = new WeakReference(testClass, weakRQ);//Weak reference System.out.println("pr: before gc: " + pr.get() + ", " + phantomRQ.poll()); System.out.println("wr: before gc: " + wr.get() + ", " + weakRQ.poll()); testClass = null;//Remove strong references System.gc();//Execute the first gc try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("pr: after first gc: " + pr.get() + "," + phantomRQ.poll()); System.out.println("wr: after first gc: " + wr.get() + "," + weakRQ.poll()); System.gc();//Execute the second gc try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("pr: after second gc: " + pr.get() + "," + phantomRQ.poll()); System.out.println("wr: after second gc: " + wr.get() + "," + weakRQ.poll()); } } class TestClass {//The class of the recycled object overrides the finalize method @Override protected void finalize() throws Throwable{ super.finalize(); System.out.println("finalize method executed"); } }

The results are as follows:

pr: before gc: null, null wr: before gc: TestClass@279f2327, null finalize method executed pr: after first gc: null,null wr: after first gc: null,java.lang.ref.WeakReference@2ff4acd0 pr: after second gc: null,java.lang.ref.PhantomReference@54bedef2 wr: after second gc: null,null

To facilitate observation, we remove the line finalize method executed and adjust the order of execution results:

pr: before gc: null, null pr: after first gc: null,null pr: after second gc: null,java.lang.ref.PhantomReference@54bedef2 wr: before gc: TestClass@279f2327, null wr: after first gc: null,java.lang.ref.WeakReference@2ff4acd0 wr: after second gc: null,null

From the above results, we can see that for the weak reference queue, after the first gc, the reference object poll ed from the queue is not empty, indicating that the weak reference object is indeed added to the weak reference queue after the first gc; On the contrary, for virtual references, after the first gc, the object obtained from the virtual reference queue is null, and the virtual reference object is not obtained from the virtual reference queue until the second gc.

2. Object collection without overriding the finalize method

import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class TestPhantomReference { public static void main(String[] args) { ReferenceQueue phantomRQ = new ReferenceQueue();//Virtual reference queue ReferenceQueue weakRQ = new ReferenceQueue();//Weak reference queue StringBuilder builder = new StringBuilder();//Recycled object PhantomReference pr = new PhantomReference(builder, phantomRQ);//Virtual reference WeakReference wr = new WeakReference(builder, weakRQ);//Weak reference System.out.println("pr: before gc: " + pr.get() + ", " + phantomRQ.poll()); System.out.println("wr: before gc: " + wr.get() + ", " + weakRQ.poll()); builder = null;//Remove strong references System.gc(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("pr: after first gc: " + pr.get() + "," + phantomRQ.poll()); System.out.println("wr: after first gc: " + wr.get() + "," + weakRQ.poll()); System.gc(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("pr: after second gc: " + pr.get() + "," + phantomRQ.poll()); System.out.println("wr: after second gc: " + wr.get() + "," + weakRQ.poll()); } }

The execution results of the above code are as follows:

pr: before gc: null, null wr: before gc: , null pr: after first gc: null,java.lang.ref.PhantomReference@279f2327 wr: after first gc: null,java.lang.ref.WeakReference@2ff4acd0 pr: after second gc: null,null wr: after second gc: null,null

We can see that since StringBuilder does not override the finalize method, both virtual Reference queues and weak Reference queues are added to their Reference queues after the first garbage collection. It should be added here that after the first garbage collection, the Reference object is added to the Reference queue, and the object pointed to by the Reference object (StringBuilder) has been recycled.

Reference blog:

1,https://blog.csdn.net/yizishou/article/details/71194944   Weak references, virtual references, finalize practices, and their order

2,https://blog.csdn.net/qiaoguaping9272/article/details/82078643    Deep understanding of JVM (3) -- Talking about reference and finalize() method

3,https://www.jianshu.com/p/e5364c05cc80    Understand java strong reference, soft reference, weak reference and virtual reference through examples

4,https://blog.csdn.net/a4171175/article/details/90749839 finalize()

5,https://blog.csdn.net/Mark2When/article/details/59162810   Java reclaims the tag of objects and the secondary tag process of objects

6,https://www.jianshu.com/p/651e7011c018 FinalReference

21 November 2021, 23:01 | Views: 4620

Add new comment

For adding a comment, please log in
or create account

0 comments