Detailed explanation of GC principle

Posted by Rother2005 on Wed, 15 Dec 2021 15:30:06 +0100

GC (garbage collector) is the memory collector of the JVM. When the memory used by the application is insufficient, it will lead to OOM (out of memory).

The GC provided by java can automatically monitor whether the object exceeds the scope, so as to achieve the purpose of automatic memory recycling (Java does not provide a method to actively release the allocated memory).

Java GC will automatically manage memory. If you want to actively request memory recycling, you can call the following methods:

  • System.gc()
  • Runtime.getRuntime().gc()

2, How GC manages memory (GC algorithm)

When we create an object, the JVM will allocate a piece of memory for the object we create. How to manage this memory?

Reference counting method (the algorithm can not solve the circular reference, resulting in memory can not be released, and GC does not use this method anymore):

When the object is created, the initialization count is 1; Whenever a place references it, the count is + 1; Whenever a local reference fails, the count is - 1;

Accessibility analysis (GC):

The basic idea of this method is to use a series of objects called "GC Roots" as the starting point and search downward from these points. The search path is called "reference chain". When an object reaches GC If the Roots do not have any reference chain (that is, GC Roots cannot reach the object), the object is proved to be unavailable, and the memory of the object can be reclaimed.

Other algorithms (will be analyzed separately later)

3, GC Roots

How to select GC Roots? In Java, GC Roots include the following:

  • Classes loaded by the system class loader (bottstrap);
  • The reference object in the JVM virtual machine stack (the local variable area in the stack frame, also known as the local variable table);
  • The object referenced by the class static attribute in the JVM method area;
  • Objects referenced in the JVM constant pool;
  • The object referenced by JNI (Native method) in the JVM local method stack;
  • Active thread

A legend of GC Roots is given below:

gc roots.png

The above figure shows the reference chain of GC Roots. Obj8, obj9 and obj10 have no reference chain to GC Roots objects, so they will be recycled.

4, Object reference and memory recovery principle based on Reachability Analysis

references.png

For the reachability analysis algorithm, the unreachable object is not "must die". To declare the death of an object, it needs to go through at least two marking stages:

  1. If an object is found to have no reference chain connected to GC Roots after accessibility analysis, it will be marked for the first time and filtered. The filtering conditions are as follows:

Is it necessary to execute the finalize method of the object

  • If the object does not overwrite the finalize method or the finalize method has been executed by the JVM, it is considered unnecessary to execute the finalize method of the object, that is, the object will be recycled;
  • If the object overrides the finalize method and has not been executed, the object will be placed in a queue called F-Queue, and then the JVM will automatically establish a Finalizer thread with low priority for execution. The JVM does not need to wait for the thread to finish execution, that is, the JVM is only responsible for establishing a thread, and other things will be handled by the thread;
  1. Mark the objects in the F-Queue for the second time:
  • If the object saves itself in the finalize method, that is, it is associated with the GC Roots reference chain (such as assigning the this keyword to other variables), the object will be removed from the collection of "to be recycled" at the second marking;
  • If the object does not save itself, it will be recycled;

The following code demonstrates how an object can save itself in the finalize method (then, it can only save itself once and still be recycled the second time):

public class GC { 
     public static GC SAVE_HOOK = null; 
     public static void main(String[] args) throws InterruptedException {
          // New object because SAVE_HOOK points to this object, and the object's state is (reachable,unfinalized)
          SAVE_HOOK = new GC(); 
          // Save_ When hook is set to null, the object just created is unreachable because no handle points to it,
          // The status of the object is (unreachable, unfinalized)
         SAVE_HOOK = null; 
         // Force the system to perform garbage collection. The system finds that the object just created is in unreachable state,
         // It is detected that the class of this object overrides the finalize method, so this object is put into the F-Queue queue,
         // The low priority thread executes its finalize method, and the state of the object becomes unreachable and finalized
         // Or (finalizer reachable, finalizable)
         System.gc(); 
         // sleep, which is designed to provide an opportunity for low priority threads to fetch objects from the F-Queue queue and execute their finalize method.
         // After executing the super. In the finalize method of the object When finalize (), the state of the object becomes unreachable, finalized,
         // But then save is executed in the finalize method_ HOOK = this; In this sentence, a handle points to the object again, and the object can reach again.
         // Therefore, the state of the object becomes a reachable, finalized state.
         Thread.sleep(500);
         // Here, the landlord said that it should be reasonable for the object to be in a reachable, finalized state. The finalized method of the object is executed,
         // Therefore, it is the finalized state. And because save is executed in the finalize method_ Hook = this sentence was originally an unreachable object,
         // It's reachable again.
         if (null != SAVE_HOOK) { //At this point, the object should be in the (reachable, finalized) state 
             // This sentence will be output. Note that the object is resurrected after finalizing from unreachable.
             System.out.println("Yes , I am still alive"); 
         } else { 
             System.out.println("No , I am dead"); 
         } 
         // Save again_ Hook is empty. At this time, the status of the object just resurrected becomes unreachable and finalized
         SAVE_HOOK = null; 
         // Once again, the system forces the system to collect garbage. At this time, the system finds that the object is unreachable. Although the finalize method is overwritten, it has been executed, so it is collected directly.
         System.gc(); 
         // Provide opportunities for the system to recycle garbage
         Thread.sleep(500); 
         if (null != SAVE_HOOK) { 
             // This sentence will not be output because the object has completely disappeared.
             System.out.println("Yes , I am still alive"); 
         } else { 
             System.out.println("No , I am dead"); 
         } 
     } 
     @Override 
     protected void finalize() throws Throwable { 
         super.finalize(); 
         System.out.println("finalize method executed!"); 
        // This sentence makes the state of the object from unreachable to reachable, which is the resurrection of the object
         SAVE_HOOK = this; 
     } 
 }  

The operation results are as follows:

    finalize method executed!
    yes, i am still alive
    no, i am dead

Thus, the object can only save itself once, because the finalize method of the object can be called by the JVM at most once.

In addition, we can also know that the this (the first item in the local variable table) reference of a heap object will always exist. In the method body, the this reference can be assigned to other variables, so that the objects in the heap can be referenced by other variables, that is, they will not be recycled.

5, Method of garbage collection in the area

  1. Garbage collection in the method area mainly includes two parts:
  • Discard constants;
  • Useless classes;

Since garbage collection is carried out, you need to judge which are obsolete constants and which are useless classes?

How to judge the obsolete constant? Take literal collection as an example. If a String "abc" has entered the constant pool, but no String object in the current system references a literal called "abc", then "abc" will be removed from the constant pool by the system if garbage collection occurs and necessary. The symbolic references of other classes (interfaces), methods and fields in the constant pool are similar.

How to judge useless classes? The following three conditions need to be met:

  • All instances of this class have been recycled, that is, there are no instances of this class in the Java heap;
  • The ClassLoader that loads this class has been recycled;
  • The corresponding Java. Java Lang. class object is not referenced anywhere, and the method of this class cannot be accessed anywhere through reflection;