Which objects can be recycled in JVM garbage collection

1. Background

Compared with C and C++, the biggest feature of Java language is that it does not require programmers to manually apply for and release memory by themselves. All of this is done by JVM. In Java, the data area at runtime is divided into a program counter, a Java virtual machine stack, a local method stack, a method area, and a heap. Among them, the program counter, virtual machine stack, and local method stack are thread-private, and are automatically released after the thread is destroyed. The behavior of garbage collection occurs in the heap and method areas, mainly the heap, and the objects stored in the heap are mainly objects. Then naturally there will be several questions, which objects can be recycled? How to recycle? This article mainly discusses the first question and the recycling strategy of several references in Java by JVM.

2. How to judge whether an object can be recycled

2.1 Reference counting method

The main idea is to add a reference counter to the object. If the object is referenced once, the counter will increase by 1; if it is no longer referenced, the counter will decrease by 1. If the reference counter of an object is 0, indicating that no one uses the object, then the object can be recycled. This method is relatively simple to implement and efficient, and it is effective in most cases. However, this method has a loophole. For example, A.property = B, B.property = A, two objects A and B refer to each other, and no other objects refer to A and B. According to the idea of ​​reference counting, the reference counters of A and B objects are not 0 and cannot be released, but the actual situation is that no one uses them for A and B, which causes a memory leak. Therefore, although the reference counting method is simple to implement, it is not a perfect solution, and the actual Java does not use it.

2.2 Reachability analysis algorithm

The main idea is: first determine a series of objects that cannot be recycled, namely GC Roots. Then, starting from these GC Roots, search down to find the objects it directly and indirectly refer to. Finally, if an object is not directly or indirectly referenced by GC Roots, then the object can be recycled. This method can effectively solve the problem of circular references. In practice, Java also uses this judgment method. So the question is, which objects can be used as GC Roots? Here you can use the MAT tool to observe. Run the following demo:

import java.util.concurrent.TimeUnit;

public class GCRootsTest {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        TimeUnit.SECONDS.sleep(100);
    }
}

When the main thread sleep in the terminal window execute jmap -dump: format = b, live , file = heapdump.bin 2872 command , raw heap dump snapshot dump file, where 2872 is the process id, you can use the jps command. Then use the MAT tool to open the dump file, you can clearly see that there are four types of objects that can be used as GC Roots, which are described in detail below.

The first category, the system class object (System Class). For example, the Class object of java.lang.String is well understood. If these core system class objects are recycled, the program will no longer be able to run.

The second category, objects referenced by native methods.

The third category is the object being referenced in the active thread. It can be seen that the Object object pointed to by the variable o in the code can be regarded as GC Roots.

The fourth category is the object being locked.

3. Several references in Java

In the reachability analysis algorithm, to determine whether an object can be recycled, it mainly depends on whether a reference to the object can be found from the GC Roots. There are four kinds of references in java. According to the strength of the reference, they are Strong Reference, Soft Reference, Weak Reference, and Phantom Reference. In this way, different recycling strategies can be adopted for objects pointed to by different references. For example, if a strong reference points to an object, then this object will definitely not be recycled, even if OOM occurs. As for the object pointed to by the weak reference, as long as garbage collection occurs, the object will be recycled. The following is a detailed introduction to the usage of different references.

3.1 Strong references

The so-called strong reference is the one that is used most often, similar to the reference of Object obj = new Object(). The garbage collector will never reclaim objects pointed to by strong references.

3.2 Soft references

Soft references, use the SoftReference class in Java to implement soft references. In the following code, softReference refers to an Object object as a soft reference, and the otherObject variable can be indirectly referenced to the Object object through the get method of soft reference.

    public static void main(String[] args) {
        // 软引用
        SoftReference<Object> softReference = new SoftReference<>(new Object());
        Object otherObject = softReference.get();
    }

For the object pointed to by the soft reference, when the memory is not enough, the object will be recycled. To demonstrate this phenomenon, set the heap memory of the JVM to 10M (-Xms10M -Xmx10M). The main logic of the following code is: add 5 SoftReference objects to a List collection, where each SoftReference object points to a byte array with a size of 2M, after the addition is complete, iterate through the List and print the points pointed to by each soft reference in the List Object.

public class ReferenceTest {

    private static final int _2M = 2 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<Object>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<Object> softReference = new SoftReference<>(new byte[_2M]);
            list.add(softReference);
        }

        System.out.println("List集合中的软引用:");
        for (int i = 0; i < 5; i++) {
            System.out.println(list.get(i));
        }

        System.out.println("--------------------------");
        System.out.println("List集合中的软引用指向的对象:");
        for (int i = 0; i < 5; i++) {
            System.out.println(list.get(i).get());
        }
    }
}

The result of the above code running when the heap memory is 10M is shown in the figure below. You can see that the objects pointed to by the first three soft references have been recycled by the garbage collector. The reason is that the heap memory is not enough, and the objects pointed to by the soft references are recycled.

Under normal circumstances, the object pointed to by the soft reference is recycled, then the soft reference has no meaning and should be recycled by the garbage collector. In order to achieve this effect, soft references are usually used with reference queues. The usage is as shown in the following code. Associate the soft reference with the reference queue, so that when the object pointed to by the soft reference is recycled, the soft reference will automatically be added to the reference queue. At this time, a certain strategy can be used to recycle these soft reference objects. .

public class ReferenceTest {

    private static final int _2M = 2 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<Object>> list = new ArrayList<>();
        // 引用队列
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        for (int i = 0; i < 5; i++) {
            // 同时将软引用关联引用队列,当软引用指向的对象被回收时,该软引用会加入到队列
            SoftReference<Object> softReference = new SoftReference<>(new byte[_2M], queue);
            list.add(softReference);
        }

        // 移除List中,指向对象已经被回收的软引用
        Reference<?> poll = queue.poll();
        while (null != poll) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("List集合中的软引用:");
        for (SoftReference<Object> reference : list) {
            System.out.println(reference);
        }

        System.out.println("-------------------------------------");
        System.out.println("List集合中的软引用指向的对象:");
        for (SoftReference<Object> reference : list) {
            System.out.println(reference.get());
        }
    }
}

The execution results are as follows:

3.3 Weak references

Weak citations, compared to soft citations, are weaker in citations. Whenever garbage collection occurs, the object pointed to by the weak reference will be collected. Not much to say, just go to the code. Similar to the soft-referenced demo, the only difference is that the size of each byte array has become 2K, so that the heap will definitely be stored, and garbage collection will not occur.

public class WeakReferenceTest {
    private static final int _2K = 2 * 1024;

    public static void main(String[] args) {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            WeakReference<byte[]> reference = new WeakReference<>(new byte[_2K]);
            list.add(reference);
        }

        System.out.println("List集合中的软引用:");
        for (WeakReference<byte[]> reference : list) {
            System.out.println(reference);
        }

        System.out.println("-------------------------------------");
        System.out.println("List集合中的软引用指向的对象:");
        for (WeakReference<byte[]> reference: list) {
            System.out.println(reference.get());
        }
    }
}

run. You can see that the object pointed to by the weak reference has not been recycled.

On the basis of the above code, a garbage collection is performed artificially. The code is as follows.

public class WeakReferenceTest {
    private static final int _2K = 2 * 1024;

    public static void main(String[] args) {
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            WeakReference<byte[]> reference = new WeakReference<>(new byte[_2K]);
            list.add(reference);
        }

        System.gc(); // 手动垃圾回收
        System.out.println("List集合中的弱引用:");
        for (WeakReference<byte[]> reference : list) {
            System.out.println(reference);
        }

        System.out.println("-------------------------------------");
        System.out.println("List集合中的弱引用指向的对象:");
        for (WeakReference<byte[]> reference: list) {
            System.out.println(reference.get());
        }
    }
}

run. It is found that the objects pointed to by the weak references have been recycled. Like soft references, weak references can also be used in conjunction with reference queues, so I won’t repeat them here.

3.4 Phantom references

Different from soft references and virtual references, virtual references must be used with reference queues, and objects pointed to by virtual references cannot be obtained through virtual references. In Java, the virtual reference is represented by the PhantomReference class. From the source code of PhantomReference, it can be seen that the get method of calling the virtual reference always returns null, and PhantomReference only provides a parameterized constructor that contains the reference queue, which means that the virtual reference Must be used in conjunction with the reference queue.

public class PhantomReference<T> extends Reference<T> {

    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

Since the object it points to cannot be obtained through a phantom reference, what is the use of a phantom reference? In fact, the only purpose of associating a virtual reference to an object is to receive a system notification when the object is garbage collected. When the garbage collector is about to recycle an object, if it finds that there is a phantom reference associated with it, it will add the phantom reference to the reference queue after the garbage collection. Before the associated phantom reference is dequeued, it will not be completely destroyed. Object. The above description is still not easy to understand. In fact, a classic use scenario of virtual references is to use them in association with the DirectByteBuffer class. The DirectByteBuffer class uses off-heap memory (in the server memory, except for the part occupied by the JVM), which eliminates the need for data to be copied to the kernel, so the efficiency is much higher than that of ByteBuffer (the focus here is virtual references. I want to understand DirectByteBuffer The underlying principle of the class, you can find resources on the Internet), its memory diagram is as follows.

Although the DirectByteBuffer class is very efficient, because the garbage collector of the JVM of the off-heap memory cannot be recycled, the off-heap memory used by the DirectByteBuffer class should be handled carefully, otherwise it is very easy to cause server memory leaks. To solve this problem, phantom references come in handy. The creation and recycling of the DirectByteBuffer class is mainly divided into the following steps

  • When creating a DirecByteBuffer object, a Cleaner virtual reference object will be created at the same time, pointing to itself, and a Deallocator object will be passed to Cleaner at the same time

  •  The parent class of the Cleaner class is PhantomReference, and the grandfather class is Reference. The Reference class will start a ReferenceHandler thread when it is initialized

  • When the DirectByteBuffer object is recycled, the Cleaner object will be added to the reference queue
  • At this time, the ReferenceHandler thread will call the clean method of the Cleaner object to complete the recovery of the off-heap memory

  • The clean method calls the run method of Deallocator, and finally completes the recovery of off-heap memory through the Unsafe class

In summary, it is one sentence, associate DirectByteBuffer objects with virtual references. When DirectByteBuffer is recycled, the virtual reference objects will be added to the reference queue, and then the virtual reference object will release the off-heap memory. (Interested or partners can follow the source code of DirectByteBuffer below)

4. Summary

  • JVM uses reachability analysis algorithm to determine which objects in the heap can be recycled.
  • There are four main types of objects that can be used as GC Roots: system objects, objects referenced by Native methods, objects referenced by active threads, and objects being locked.
  • There are four main types of references commonly used in Java: strong references, soft references, weak references and phantom references. The JVM has different recycling strategies for objects pointed to by different references.
  • For objects pointed to by strong references, the garbage collector will not recycle them, even if OOM occurs.
  • For the object pointed to by the soft reference, when the memory is insufficient, the garbage collector will reclaim it. This feature can be used to implement caches, and the JVM will automatically clean up these caches when the memory is insufficient.
  • For objects pointed to by weak references, when garbage collection occurs, the garbage collector will recycle them.
  • For virtual references, they must be used in conjunction with the reference queue, and the object pointed to by the virtual reference cannot be obtained through the virtual reference. The only purpose of associating a virtual reference with an object is to receive a system notification when the object is garbage collected.

Guess you like

Origin blog.csdn.net/weixin_40988088/article/details/114769014