“JVM” How does the Java virtual machine determine whether the object is dead?

How does the “JVM” Java virtual machine determine whether the object is dead?

References & Acknowledgments

Did the subject die? Detailed explanation of reference counting and accessibility analysis Programming development sharer

Interviewer: Tell me how to judge the life and death of an object in Jvm? rio ii

JVM advanced features and best practice (3) – how to judge that the object is dead? select you from me

Article directory

  • “JVM” How does the Java virtual machine determine whether the object is dead?
    • @[toc]
    • I. Introduction
    • 2. Reference counting method
    • 3. Accessibility Analysis Algorithm
    • 4. What is “quote”
    • 5. Determine whether the subject is dead
    • 6. Recycling method area
    • 7. Summary

1. Foreword

We all know that there is a very big difference between Java and C++, which is that Java has an automatic garbage collection mechanism. After more than half a century of development, Java has entered the era of “automation”, allowing users to only focus on the development of business logic You don’t need to worry about memory usage. So why do we still need to learn Java’s garbage collection mechanism? The reason is simple: we don’t want to stop at the primary level of “addition, deletion, modification and checking engineer”. Once memory overflow, memory leak and other problems occur in the program, we can better adjust and optimize our code with the knowledge we have mastered. Before learning this chapter, by default, everyone has understood and mastered the functions of the five areas of the Java memory runtime: Method area, Java heap, virtual machine stack, local method stack, and program counter.

Just imagine, what is the first thing GC (garbage collection mechanism) does when cleaning up memory? It must be determined whether the objects in memory are dead, that is, they will never be used again, and then these objects will be recycled. There are usually two ways to judge whether an object is dead: reference counting and reachability analysis.

2. Reference counting

To use the reference counting method, you must first add a counter to each object. Once the object is referenced somewhere, the counter of the object will be incremented by 1. If the reference is invalid, the counter will be decremented by 1. In this way, when the counter is 0, it means that this object is not referenced anywhere. This method is simple to implement and has high judgment efficiency. It is a relatively good method in most cases. However, the reference counting method is not used to manage memory in the Java virtual machine. The main reason is that it is difficult to solve the problem of mutual reference between objects. If two corresponding objects refer to each other, their reference counts are not 0, and eventually They cannot be recycled.

(For example, the field of object A refers to object B, and the field of object B refers to field A. At this time, null is assigned to object A, and the reference counters of B and their reference counters are not 0, which means that the object is still being referenced. but actually no references anymore)

  • Pros: Marking “garbage” objects is simple and efficient
  • Disadvantage: The increase and decrease processing frequently consumes cpu calculations, the counter occupies a lot of bits and wastes space, and the most important disadvantage is that cannot solve the problem of circular reference. Because reference counter algorithms are difficult to solve the problem of circular references, mainstream Java virtual machines do not use reference counter algorithms to manage memory.

Prove that Java does not use reference counting algorithm

 public class ReferenceCountTest {<!-- -->
     //used internal memory
     private static final byte[] MEMORY = new byte[1024 * 1024 * 2];
 ?
     private ReferenceCountTest reference;
 ?
     public static void main(String[] args) {<!-- -->
         ReferenceCountTest a = new ReferenceCountTest();
         ReferenceCountTest b = new ReferenceCountTest();
         // circular reference
         a.reference = b;
         b. reference = a;
 ?
         a = null;
         b = null;
 // System.gc();
     }
 }

image-20230319013118967

3. Accessibility analysis algorithm

In most mainstream languages, this method is used to judge whether the object is alive. The idea of this algorithm isuse a series of objects called “GC root” as the starting point, and start searching downward from these nodes. The path traveled is called the reference chain. If an object is not connected to the GC root node through the reference chain, it proves that the object is unavailable. As shown in the figure below, GC roots are the root nodes, and all Objects that can be connected to the GC root through the reference chain are used objects . However, the red Object cannot be connected to the root node in any way, so it is determined that the red Object is a recyclable node.

image-20230319014346973

After understanding the reachability analysis method, you may ask what is the GC root object? In the JAVA language, objects that can be used as GC roots include the following:

  • Objects referenced in the virtual machine stack (local variable table in the stack frame).
  • The object referenced by the class static property in the method area.
  • The object referenced by the constant in the method area.
  • The object referenced by JNI (Java Native Interface) in the native method stack.

The above four do not need to be memorized by rote, because the method area, the virtual machine stack and the local method stack save the references to the variables defined in the class and the method, since they are self-defined variables, they must be useful.

4. What is “reference”

We know that data types are divided into two categories in java: basic types and reference types. The definition of reference in java is: if the value stored in the reference type data represents the starting address of another piece of memory, it is said that this piece of memory represents a reference. for example:

Person p = new Person();

We often see the writing of the above code, where new Person(); after the equal sign is a real object, and all the content is stored in the java heap memory, while before the equal sign p is just a synonym for the real content, stored in the virtual machine stack memory, it stores only an address, which is the starting position of new Person(); in the heap memory, so p is a quote. According to this understanding, java objects can only be divided into two cases: being referenced and not being referenced. But after JDK1.2, java has expanded the concept of references, which are divided into four types: strong, soft, weak, and virtual, and the strength gradually decreases in turn.

  • Strong reference: that is, the reference method we often see, such as defined in the method: Object obj = new Object();, the real object “new Object()” is stored in the java heap, where “obj” represents a reference , which stores the starting address of “new Object()” in the java heap. As long as the reference is still there, the garbage collector will not reclaim the referenced object.
  • Soft reference: It is used to describe some useful but not necessary objects. We can use the SoftReference class to implement soft references. For objects associated with soft references, these objects will be included in the recovery scope before the system will experience a memory overflow exception. If the memory is still insufficient after recycling, a memory overflow exception will be reported.
  • Weak reference: It is used to describe non-essential objects, using the WeakReference class to implement weak references. It can only survive until the next garbage collection occurs. When the garbage collection mechanism starts, regardless of whether there will be memory overflow, the objects associated with the weak reference will be reclaimed.
  • Phantom reference: A kind of reference relationship with the least sense of existence, which can be realized through the PhantomReference class. Existence or non-existence has almost no effect, and an object instance cannot be obtained through a virtual reference. The only purpose of existence is to receive a system notification after being recycled by the garbage collector.

We can control the “strong, weak, and virtual” four references of objects through code, which is conducive to JVM garbage collection. So after knowing the above knowledge, let’s explore whether the object will die?

5. Determine whether the object is dead

As mentioned before, after the reachability analysis, the unreachable objects found will be recycled by the garbage collector. So, will the unreachable objects be recycled? The answer is not necessarily. At this time, they are in the stage of “death reprieve”. If they have to “appeal”, they may be acquitted. How did they save themselves? After the reachability analysis, it is found that some objects do not have a reference chain connected to the GC root. The object will be marked once and then filtered. The filtering condition is to judge whether the object needs to be finalized () method (each object has this method by default), but if the object does not override the finalize() method or the finalize method of the object has been called once by the virtual machine, it will be regarded as “no need to execute”, garbage The recycler can recycle directly. (This paragraph is the process of self-salvation, not the key point to understand)

If the object is determined to be necessary to execute the finalize() method, then the virtual machine will place the object in a F-Queue, and then a dedicated Finalizer thread will execute the finalize() method of the object. )method. We can “self-save” the object in this method, that is, re-establish an association with any object on the reference chain, such as assigning this to a variable of a certain class, or a member variable of an object, then in the It will be removed from the “to be recycled” collection during the second mark. Let’s look at a case to understand.

public class FinalizeEscapeGC {<!-- -->

    /**
     * Review of knowledge points:
     * 1. The method area stores the basic information of the class, static variables, compiled code, and constant pool
     * 2. GC root can be an object referenced by a static variable in the method area
     * 3. The finalize() method of an object will only be automatically called once by the system at most.
     * */
    //Create a static variable
    public static FinalizeEscapeGC SAVE_HOOK = null;

    @Override
    protected void finalize() throws Throwable {<!-- -->
        super. finalize();
        System.out.println("The program executes the finalize() method");
        SAVE_HOOK = this;//Assign yourself to a static variable to save yourself, connect to GC root (review of detailed knowledge points)
    }

    public static void main(String[] args) throws InterruptedException {<!-- -->
        SAVE_HOOK = new FinalizeEscapeGC();
        // Prepare to kill the object for the first time
        SAVE_HOOK = null;//Empty the object, it is reasonable to be reclaimed by GC, but this object implements the finalize() method and realizes self-saving
        System.gc();//Execute GC
        Thread.sleep(500);//Because the priority of the Finalizer thread is relatively low, the main thread is temporarily dormant and so on
        if (SAVE_HOOK!=null){<!-- -->
            System.out.println("Hahaha, I'm still alive");
        } else {<!-- -->
            System.out.println("No, I'm farting");
        }
        System.out.println("--------------------------");

        // Prepare to kill the object for the second time (same as the above code)
        SAVE_HOOK = null;//Empty the object, at this time the finalize() method has been automatically executed once
        System.gc();//Execute GC
        Thread.sleep(500);//Because the priority of the Finalizer thread is relatively low, the main thread is temporarily dormant and so on
        if (SAVE_HOOK!=null){<!-- -->
            System.out.println("Hahaha, I'm still alive");
        } else {<!-- -->
            System.out.println("No, I'm farting");
        }
    }

}

image-20230319020910655

Rewriting finalize() is expensive, and the execution order of each object cannot be determined, so it is not recommended

6. Recovery method area

Many people think that there is no garbage collection in the method area (permanent generation). The Java virtual machine specification does say that the virtual machine may not be required to implement garbage collection in this area, and that garbage collection is performed in this area. The cost performance of garbage collection is not very high. There are two main parts to garbage collection in this area: Deprecated constants and Useless classes.

  • obsolete constant
    When a constant in the constant pool is not referenced anywhere, if memory recovery occurs at this time, and if necessary, the constant will be cleared out of the constant pool by the system.

  • useless class

    • All instances of this class have been recycled.
    • The ClassLoader that loaded this class has been recycled.
    • The java.lang.Class object corresponding to this class is not referenced anywhere.

    Useless classes that meet the above three conditions can be recycled. What is said here is only “can”, not the same as the object, it will inevitably be recycled if it is not used.

7. Summary

Through this article, we learned about two methods for judging the death of an object: the reference counting method and the reachability analysis algorithm. At the same time, we understood the specific steps for judging the death of an object. Have a further understanding of the “reference” related overview.

Even objects that are unreachable in the reachability analysis algorithm do not have to die. To actually declare the death of an object, it goes through at least two marking processes.
(1) If the object finds that there is no reference chain linked to GCRoots after the reachability analysis, it will be marked for the first time and screened. The screening condition is whether the object needs to execute the finalize () method, if it is the first Override the finalize() method once, then enter the second markup.
(2) Put the above objects into the F-Queue queue, and then the virtual machine executes a low-priority Finalizer thread to perform the marking process. If the object re-references any object on the chain at this time, it will be removed from the queue and the object will become active.