Understand the four reference types in Java through examples

Java reference type


There are four reference types in Java: strong, soft, weak and virtual. The Strong Reference class does not exist. The default objects are all Strong Reference types. Compared with the background of WeakReference, SoftReference and phantom reference, it is called Strong Reference. The class diagram is as follows:

Strong reference

interpretation

If the GC reachability analysis result of the JVM garbage collector is reachable, it means that the reference type is still referenced, and such objects will never be recycled by the garbage collector. Even if the JVM has an OOM, they will not be recycled. If the accessibility analysis result of GC is unreachable, it will be recycled during GC.

Code demonstration

Examples of strong references are as follows:
String strong = "strongReference";

Soft reference

interpretation

A soft reference is a reference type that has a slightly weaker lifecycle than a strong reference. When the JVM memory is sufficient, the soft reference will not be recycled by the garbage collector. Only when the JVM memory is insufficient, it will be recycled by the garbage collector.

Code demonstration

    public void testSoftReference(){
        // Allocate 10 M memory to soft
        SoftReference<byte[]> soft = new SoftReference<>(new byte[1024*1024*10]);
        // View soft objects
        System.out.println("new:  "+soft.get());
        // When memory is sufficient, a gc is recommended
        System.gc();
        // View the soft object after gc
        System.out.println("for the first time gc: "+soft.get());
        // Apply for a strong reference type here so that the sum of allocated memory is greater than the maximum available memory of the jvm
        byte[] strong = new byte[1024*1024*10];
        // View soft objects
        System.out.println("insufficient memory: "+soft.get());

    }

Before executing the code, set the maximum heap memory to 15M through - xms15m, which is the key. If this step is not set, the following results will not be reproduced.
The print result is:

It can be seen from the example that after the first gc, the soft object is not recycled because the heap memory is sufficient, but when the memory is insufficient, the soft object is recycled.

purpose

Therefore, soft references are generally used to implement some memory sensitive caches. As long as the memory space is enough, the objects will not be recycled. For example, large image files are cached when processing images.

Other issues

  1. When memory is low, if I declare another soft reference, what is a virtual reference, will the first soft reference be recycled?
    Answer: Yes

Weak reference

interpretation

A weak reference is a reference with a shorter life cycle than a soft reference. Its life cycle is very short. No matter whether the current memory is sufficient or not, it can only survive until the next garbage collection.
In addition, a weak reference can be used in conjunction with a reference queue. If the object referenced by the weak reference is garbage collected, the JVM will add the weak reference to the associated reference queue.

example

A typical use of weak references is in ThreadLocal. For specific examples:
Introduction to ThreadLocal

Virtual reference

interpretation

Compared with weak references, virtual references are weaker than weak references. They can't even get the pointed value through virtual references. null is directly returned in the source code;

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }

Compared with weak references, virtual references must be used together with a ReferenceQueue. When the object pointed to by the virtual reference is collected by GC, the object will not be released immediately. At this time, the phantom reference will be put into the associated ReferenceQueue. The space occupied by the object will be officially released only after the virtual reference in the corresponding queue is processed.

Note: in Java 8 and earlier versions, the object pointed to by the virtual reference will not be recycled until the virtual reference is recycled. In Java 9 and later versions, virtual references have no impact on the survival of objects.

Code example

public class TestReference {
    private static final List<Object> list = new ArrayList<>();
    private static final ReferenceQueue<M> RQ = new ReferenceQueue();
    
    static class M{
        @Override
        protected void finalize() throws Throwable {
            System.out.println("I was recycled");
        }
    }
    
    public void testPhantomReference(){
        PhantomReference<M> phantomReference = new PhantomReference(new M() ,RQ);
        System.out.println("Get virtual reference object:"+ phantomReference.get());
        new Thread(()->{
            while(true){
                list.add(new byte[1024*1024*2]);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(() -> {
            while (true) {
                Reference poll = RQ.poll();
                if (poll != null) {
                    System.out.println("Virtual reference recycled:" + poll);
                }
            }
        }).start();
       	// Blocking process
        Scanner scanner = new Scanner(System.in);
        scanner.hasNext();
    }
}

Execute this Code:

At this time, the finalize method of the object M pointed to by the virtual function has been executed, that is, the object has been recycled, and the poll function is also obtained in the queue.

VisualVM checks the memory status and explicitly clears the memory usage before and after virtual references

In addition, you can view the memory usage of the corresponding JVM through visual VM:

    static class M{
        byte[] value = new byte[1024*1024*6];
        @Override
        protected void finalize() throws Throwable {
            System.out.println("I was recycled");
        }

    }
    public void testPhantomReference2(){
        PhantomReference<M> phantomReference = new PhantomReference(new M() ,RQ);
		System.gc();
//        phantomReferenc
//        Reference poll 
//        poll = null;   
        System.gc();
        byte[] b = new byte[1024*1024*8];

        Scanner scanner = new Scanner(System.in);
        scanner.hasNext();
    }

When this code is executed, even if System.gc() is executed, the variable holding the 6M declared in this M in the old generation pointed to by the virtual reference still exists. When 8M space is newly declared, due to insufficient space, an error is directly reported in OOM.

When comments exist, perform gc:

Remove the comment, explicitly set the empty reference, and continue gc

Tags: Java

Posted on Tue, 21 Sep 2021 20:12:45 -0400 by luxluxlux