One of the java reference types you must know — weak references

definition

Weak reference is a reference created by using WeakReference. Weak reference is also used to describe non essential objects. It is a weaker reference type than soft reference. When a GC occurs, as long as a weak reference is found, the object will be recycled regardless of whether the system heap space is sufficient.

explain

Weak references are very weak from the name. Once the object pointed to by this reference is scanned during GC, it can not escape the fate of being recycled.

However, the object pointed to by the weak reference may not be recycled immediately. If the weak reference object is large and goes directly into the old age, it can live quietly until the full GC is triggered. Therefore, the weak reference object may exist for a long time. Once a weakly referenced object is collected by the garbage collector, it will be added to a reference queue (if any).

The class corresponding to weak reference is WeakReference, for example:

String s = new String("Frank");    
WeakReference<String> weakRef = new WeakReference<String>(s);
s = null;

Here, when we set s to null, the string object has only a weak reference to it.

Note that before the garbage collector recycles an object, the get method provided by the WeakReference class will return a strong reference to its reference object. Once the garbage collector recycles the object, the get method will return null. Therefore, in the code to obtain the weak reference object, you must judge whether it is null to avoid the NullPointerException exception causing the application to crash.

The following code will make s hold the strong reference of the object again:

s = weakRef.get();

If the object wrapped by weakref is associated with a strong reference before it is recycled, the object will become a strong reachable state.

Take a look at a simple chestnut to see when the object referenced by WeakReference is recycled:

public class WeakReferenceTest {
    private static final List<Object> TEST_DATA = new LinkedList<>();
    private static final ReferenceQueue<TestClass> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        TestClass obj = new TestClass("Test");
        WeakReference<TestClass> weakRef = new WeakReference<>(obj,QUEUE);
        //可以重新获得OOMClass对象,并用一个强引用指向它
        //oomObj = weakRef.get();

        // 该线程不断读取这个弱引用,并不断往列表里插入数据,以促使系统早点进行GC
        new Thread(() -> {
            while (true) {
                TEST_DATA.add(new byte[1024 * 100]);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
                System.out.println(weakRef.get());
            }
        }).start();

        // 这个线程不断读取引用队列,当弱引用指向的对象呗回收时,该引用就会被加入到引用队列中
        new Thread(() -> {
            while (true) {
                Reference<? extends TestClass> poll = QUEUE.poll();
                if (poll != null) {
                    System.out.println("--- 弱引用对象被jvm回收了 ---- " + poll);
                    System.out.println("--- 回收对象 ---- " + poll.get());
                }
            }
        }).start();

        //将强引用指向空指针 那么此时只有一个弱引用指向TestClass对象
        obj = null;

        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    static class TestClass {
        private String name;

        public TestClass(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "TestClass - " + name;
        }
    }
}

Set the following virtual machine parameters:

-verbose:gc -Xms4m -Xmx4m -Xmn2m

The operation results are as follows:

[GC (Allocation Failure)  1017K->464K(3584K),0.0014345 secs]
[GC (Allocation Failure)  1483K->536K(3584K),0.0017221 secs]
[GC (Allocation Failure)  1560K->648K(3584K),0.0036572 secs]
TestClass - Test
TestClass - Test
TestClass - Test
[GC (Allocation Failure)  1621K->984K(3584K),0.0011455 secs]
--- 弱引用对象被jvm回收了 ---- java.lang.ref.WeakReference@51a947fe
--- 回收对象 ---- null
null
...省略n个null和几次GC信息
[Full GC (Ergonomics)  2964K->2964K(3584K),0.0025450 secs]
[Full GC (Allocation Failure)  2964K->2964K(3584K),0.0021907 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid6860.hprof ...
Heap dump file created [3912229 bytes in 0.011 secs]
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
	at weakhashmap.WeakReferenceTest.lambda$main$0(WeakReferenceTest.java:22)
	at weakhashmap.WeakReferenceTest$$Lambda$1/764977973.run(UnkNown Source)
	at java.lang.Thread.run(Thread.java:748)

It can be seen that weak references are not recycled as soon as GC occurs.

Application scenario

If an object is only used occasionally, and you want to get it at any time, but you don't want to affect the garbage collection of this object, you should use WeakReference to reference the object.

A weak reference can be used in conjunction with a reference queue. If the object referenced by the weak reference is garbage collected, the Java virtual machine will add the weak reference to the associated reference queue.

Generally speaking, we rarely use WeakReference directly, but use weakhashmap instead. In the weakhashmap, there is a reference queue inside. The inserted elements will be wrapped into a WeakReference and added to the queue for caching.

In fact, the weakhashmap is used in Tomcat's cache:

public final class ConcurrentCache<K,V> {
    private final int size;
    private final Map<K,V> eden;
    private final Map<K,V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<>(size);
        this.longterm = new WeakHashMap<>(size);
    }

    public V get(K k) {
        // 先从eden中取
        V v = this.eden.get(k);
        if (v == null) {
            // 如果取不到再从longterm中取
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            // 如果取到则重新放到eden中
            if (v != null) {
                this.eden.put(k,v);
            }
        }
        return v;
    }

    public void put(K k,V v) {
        if (this.eden.size() >= size) {
            // 如果eden中的元素数量大于指定容量,将所有元素放到longterm中
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k,v);
    }
}

Here are two maps of Eden and longterm. If you know about the JVM heap, you can see that Tomcat uses concurrenthashmap and weakhashmap to do operations similar to generational caching.

In the put method, when inserting key value pairs, first check whether the capacity of Eden cache exceeds the set size. If not, it will be directly put into the Eden cache. If it exceeds the limit, it will lock the longterm and put all key value pairs in Eden into the longterm. Then empty Eden and insert the key value pair.

In the get method, it is also a priority to find the corresponding key from Eden. If not, it will enter the longterm cache. After finding it, it will be added to the Eden cache and returned.

Through this design, the relatively common objects can be found in the Eden cache, and those that are not commonly used (objects that may be destroyed) will enter the longterm cache. When there is no other reference to the actual object of the longterm key, the GC will automatically recycle the actual object pointed to by the weak reference in the heap and put the weak reference into its reference queue.

Comparison between weak reference and soft reference

Weak references differ from soft references in that:

Similarities: they are used to describe non essential objects.

So when to use softreference and when to use WeakReference?

If the cached object is a relatively large object and is used more frequently, it is better to use softreference because it can make the cached object have a longer life cycle.

If the cache objects are all relatively small objects, and the usage frequency is general or relatively low, it is more appropriate to use WeakReference.

Of course, if you really don't know which to choose, generally speaking, using weakhashmap for caching won't have much problem.

Summary

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>