[Java] related use of weak references

Keywords: Java

Four references to Java

1. Strong reference

The most common reference type, which is not reclaimed even if there is not enough memory.

Think: is obj1 a strong reference itself an object? Occupy memory?

public class StrongReferenceDemo {
    public static void main(String[] args) {
        //obj1 is a strong reference to the actual object from new
        Object obj1 = new Object();
        Object obj2 = obj1;
        obj1 = null;
        System.gc();
        System.out.println(obj2);
    }
}

Output result: java.lang Object@4d7e1886

2. Soft reference

If the memory is not enough, the objects pointed to by soft references will be recycled. Don't worry when the memory is enough.

package com.shin.demo05;

import java.lang.ref.SoftReference;

public class SoftReferenceDemo {
    public static void memoryEnough(){
        Object obj1 = new Object();
        //Create a soft reference to the object referred to by the strong reference obj.
        SoftReference<Object> softReference = new SoftReference<Object>(obj1);
        System.out.println(obj1);
        System.out.println(softReference.get());
        obj1 = null;
        System.gc();
        System.out.println(obj1);
        System.out.println(softReference.get());
    }
    //-Xms5m -Xmx5m deliberately manufactured OOM
    public static void memoryNotEnough(){
        Object obj1 = new Object();
        SoftReference<Object> softReference = new SoftReference<Object>(obj1);
        System.out.println(obj1);
        System.out.println(softReference.get());
        //Only soft references remain
        obj1 = null;
        try{
            byte[] bytes = new byte[300*1024*1024];
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            System.out.println(obj1);
            System.out.println(softReference.get());
        }
    }
    public static void main(String[] args) {
        memoryEnough();
        System.out.println("=========================");
        memoryNotEnough();
    }
}

Deliberately create OOM, JVM parameters - Xms5m -Xmx5m. The objects pointed to by soft references are recycled during automatic GC. Use softReference.get() to get the object pointed to.

Output results:

java.lang.Object@4d7e1886
java.lang.Object@4d7e1886
null
java.lang.Object@4d7e1886
=============
java.lang.Object@3cd1a2f1
java.lang.Object@3cd1a2f1
java.lang.OutOfMemoryError: Java heap space
	at com.shin.demo05.SoftReferenceDemo.memoryNotEnough(SoftReferenceDemo.java:25)
	at com.shin.demo05.SoftReferenceDemo.main(SoftReferenceDemo.java:36)
null
null

3. Weak reference

Objects that are only pointed to by weak references will be recycled during GC, regardless of whether there is enough memory.

import java.lang.ref.WeakReference;

public class WeakReferenceDemo {
    public static void memoryEnough(){
        Object obj1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<Object>(obj1);
        System.out.println(obj1);
        System.out.println(weakReference.get());
        obj1 = null;
        System.gc();
        System.out.println(obj1);
        System.out.println(weakReference);
        System.out.println(weakReference.get());
    }
    public static void main(String[] args) {
        memoryEnough();
    }
}

Similarly, use weakReference.get() to get the object pointed to by the weak reference. weakReference itself also occupies memory, which can be understood as an object of reference type.

Output results:

java.lang.Object@4d7e1886
java.lang.Object@4d7e1886
null
java.lang.ref.WeakReference@3cd1a2f1
null

4. Virtual reference

The object pointed to only by the virtual reference will also be recycled as long as GC. In addition, the pointed object cannot be obtained through the virtual reference.

However, virtual references can be recycled into the reference queue ReferenceQueue.

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceDemo {
    public static void main(String[] args) {
        Object obj1 = new Object();
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj1,queue);

        System.out.println(obj1);
        System.out.println(phantomReference.get());
        System.out.println(queue.poll());
        System.out.println("===========================");
        obj1 = null;
        System.gc();
        System.out.println(obj1);
        System.out.println(phantomReference.get());
        System.out.println(queue.poll());
    }

}

Output results:

java.lang.Object@4d7e1886
null
null
===========================
null
null
java.lang.ref.PhantomReference@3cd1a2f1

5. Reference queue

Weak virtual references can cooperate with the reference queue and enter the reference queue when they are recycled. Note that the queue is the reference, not the object pointed to by the reference.

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class ReferenceQueueDemo {
    public static void main(String[] args) {
        Object obj1 = new Object();
        ReferenceQueue queue = new ReferenceQueue();

        WeakReference<Object> weakReference = new WeakReference<Object>(obj1,queue);

        System.out.println(obj1);
        System.out.println(weakReference.get());
        System.out.println(queue.poll());
        System.out.println("===========================");
        //Only weak reference is left, and the actual object will be recycled by GC
        obj1 = null;
        System.gc();
        System.out.println(obj1);
        System.out.println(weakReference.get());
        //Join the team after recycling
        System.out.println(queue.poll());
    }
}

Output results:

java.lang.Object@4d7e1886
java.lang.Object@4d7e1886
null
===========================
null
null
java.lang.ref.WeakReference@3cd1a2f1

Use of weak references

Use of WeakHashMap

The implementation principle of WeakHashMap is similar to that of HashMap. The difference is that the key of WeakHashMap is a weak reference.

public class WeakHashMapDemo {
    public static void notWeak(){
        HashMap<Integer, String> map = new HashMap<>();
        Integer key = new Integer(1);
        String value = "HashMap";
        map.put(key,value);
        System.out.println(map);
        key = null;
        //Obviously, if you want to delete it, you need to use the remove() method
        System.out.println(map);
        System.gc();
        //Obviously, there are strong references inside HashMap
        System.out.println(map);
    }
    public static void weak(){
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
        Integer key = new Integer(1);
        String value = "WeakHashMap";
        map.put(key,value);
        System.out.println(map);
        key = null;
        //No, it's still there
        System.out.println(map);
        System.gc();
        //The key of WeakHashMap is a weak reference. After GC, the object originally pointed to by the key is recycled
        //But what we see here is that the entire key value pair is gone. In fact, the reference queue is used
        System.out.println(map);
    }

    public static void main(String[] args) {
        notWeak();
        weak();
    }

Output results:

{1=HashMap}
{1=HashMap}
{1=HashMap}
{1=WeakHashMap}
{1=WeakHashMap}
{}

The following paragraph is taken from Java core technology Volume 1:

The WeakHashMap class is designed to solve an interesting problem. What happens if there is a value and the corresponding key is no longer used? Suppose that the last reference to a key has died, and there is no longer any way to reference the object of this value. However, since the key does not appear in any part of the program, the key value pair cannot be deleted from the mapping. Why can't the garbage collector delete it? Isn't it the job of the garbage collector to delete useless objects?

Unfortunately, things are not so simple. The garbage collector tracks active objects. As long as the mapping object is active and all buckets are active, they cannot be recycled. Therefore, it is the responsibility of the program to delete those useless values from the long-lived mapping table. Or use WeakHashMap to do this. This data structure works with the garbage collector to delete key / value pairs when the only reference to the key comes from a hash entry.

The following is the internal operation of this mechanism. WeakHashMap uses weak references (weak references) save key. The WeakReference object saves the reference to another object, in this case, the hash key. For this type of object, the garbage collector processes it in a unique way. Usually, if the garbage collector finds that a specific object has no reference from others, it will recycle it. However, if an object can only be referenced by others For the WeakReference reference, the garbage collector will still recycle it, but will put the weak reference referring to this object in the queue. WeakHashMap will periodically check the queue to find the newly added weak reference. A weak reference in the queue means that this key is no longer used by others and has been collected. Therefore, WeakHashMap will delete the corresponding entry.

See the explungestaleentries () method in the source code of WeakHashMap for details.

Weakly referenced subclass MyEntry

What happens if a class inherits WeakReference?

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

//MyEntry inherits the WeakReference, so MyEntry itself is a weak reference
//referent and queue can be specified through the constructor of the parent class.
public class MyEntry extends WeakReference {

    //MyEntry is a weak reference and carries a value attribute
    private Object value;
    //Calling this constructor returns a weak reference of type MyEntry
    //It refers to the actual object referred to by the key, and the reference queue is queue
    public MyEntry(Object key, Object value, ReferenceQueue queue) {
        //Call the parent class constructor
        super(key,queue);
        this.value = value;
    }

    public static void main(String[] args) {
        //1. key is a strong reference
        Object key = new Object();
        Object value = new Object();
        ReferenceQueue<Object> queue = new ReferenceQueue<>();
        //2. A weak reference myEntry is created to point to the object referred to by the strong reference key
        //   When only myEntry is left, GC reclaims the actual object pointed to, and then puts myEntry into the queue
        //   Note who is queued here. The reference queue must be the reference, not the actual object pointed to by the reference.
        MyEntry myEntry = new MyEntry(key, value, queue);
        System.out.println("key:        "+key);
        System.out.println("value:      "+value);
        System.out.println("myEntry point: "+myEntry.get());
        System.out.println("queue:      "+queue.poll());
        System.out.println("===============================");
        key = null;
        System.gc();
        System.out.println("key:        "+key);
        System.out.println("value:      "+value);
        System.out.println("myEntry point: "+myEntry.get());
        System.out.println("queue:      "+queue.poll());
    }
}

Output results:

key:        java.lang.Object@4d7e1886
value:      java.lang.Object@3cd1a2f1
myEntry point: java.lang.Object@4d7e1886
queue:      null
===============================
key:        null
value:      java.lang.Object@3cd1a2f1
myEntry point: null
queue:      com.shin.demo05.MyEntry@2f0e140b

You can see that myEntry points to the object originally referred to by the key. After GC, it is recycled, and myEntry enters the reference queue.

Posted by mysqlnewjack on Thu, 18 Nov 2021 22:58:03 -0800