One of the java reference types you must know – soft reference

definition

Soft reference is a reference created by using softreference. Its strength is weaker than that of strong reference. The referenced objects will be recycled when there is insufficient memory and will not cause memory overflow.

explain

Soft reference, as the name suggests, is a "soft" reference.

When there is a strong reference between an object and GC roots, it will not be recycled by GC at any time. If there is no strong reference association between an object and GC roots, but there is a soft reference Association, the attitude of the garbage collector towards it depends on the tension of memory. If the memory space is enough, the garbage collector will not recycle the object, but if the memory space is insufficient, it will not escape the bad luck of being recycled.

If there is no strong reference between an object and GC roots, but there is a soft reference, the object is called a soft reachable object.

When the garbage collector does not recycle it, the soft reachable object, like the strong reachable object, can be accessed and used normally by the program, but it needs to be accessed indirectly through the soft reference object, and the strong reference can be re used to associate it if necessary. Therefore, soft reference is suitable for memory sensitive cache.

String s = new String("Frank");    // 创建强引用与String对象关联,现在该String对象为强可达状态
SoftReference<String> softRef = new SoftReference<String>(s);     // 再创建一个软引用关联该对象
s = null;        // 消除强引用,现在只剩下软引用与其关联,该String对象为软可达状态
s = softRef.get();  // 重新关联上强引用

Here, the variable s holds a strong reference to the string object, while softref holds a soft reference to the object. Therefore, when s = null is executed, only the soft reference is left to the string object. At this time, if a full GC occurs due to insufficient memory, the string object will be recycled.

Note that before the garbage collector recycles an object, the get method provided by the softreference class will return a strong reference to the Java object. Once the garbage thread recycles the object, the get method will return null. Therefore, in the code to obtain the soft reference object, you must first judge whether the return 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 = softRef.get();

If you point to the object pointed to by softref with a strong reference before it is recycled, the object will become strong reachable again.

Take a look at a chestnut using softreference:

public class TestA {
    static class OOMClass{
        private int[] oom = new int[1024 * 100];// 100KB
    }

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<OOMClass> queue = new ReferenceQueue<>();
        List<SoftReference> list = new ArrayList<>();
        while(true){
            for (int i = 0; i < 100; i++) {
                list.add(new SoftReference<OOMClass>(new OOMClass(),queue));
            }
            Thread.sleep(500);
        }
    }
}

Note that the type declared in ReferenceQueue is oomclass, which is consistent with the type referenced by softreference.

Set the following virtual machine parameters:

-verbose:gc -Xms4m -Xmx4m -Xmn2m

Operation results:

[GC (Allocation Failure)  1017K->432K(3584K),0.0017239 secs]
[GC (Allocation Failure)  1072K->472K(3584K),0.0099237 secs]
[GC (Allocation Failure)  1323K->1296K(3584K),0.0009528 secs]
[GC (Allocation Failure)  2114K->2136K(3584K),0.0009951 secs]
[Full GC (Ergonomics)  2136K->1992K(3584K),0.0040658 secs]
[Full GC (Ergonomics)  2807K->2791K(3584K),0.0036280 secs]
[Full GC (Allocation Failure)  2791K->373K(3584K),0.0032477 secs]
[Full GC (Ergonomics)  2786K->2773K(3584K),0.0034554 secs]
[Full GC (Allocation Failure)  2773K->373K(3584K),0.0032667 secs]
[Full GC (Ergonomics)  2798K->2775K(3584K),0.0036231 secs]
[Full GC (Allocation Failure)  2775K->375K(3584K),0.0055482 secs]
[Full GC (Ergonomics)  2799K->2776K(3584K),0.0031358 secs]
...省略n次GC信息

In testa, we use an endless loop to constantly add new objects to the list. If it is a strong reference, it will soon throw oom because of insufficient memory. Here, the heap memory size is set to 4m, and an object has 100kb. Add 100 objects in a loop, that is, almost 10m. Obviously, if a loop cannot run, it will run out of memory. Here, Because soft references are used, the JVM will recycle soft references when memory is low.

[Full GC (Allocation Failure)  2791K->373K(3584K),0.0032477 secs]

It can be seen from this that when a full GC occurs due to insufficient memory, most of the objects pointed to by soft references are recycled, freeing a lot of memory.

Because only 2m is allocated to the new generation, GC will occur soon. If your program does not see this result, please first confirm whether the virtual machine parameters are set correctly. If they are set correctly or not, change the number of cycles from 1000 to 10000 or 100000 and try.

Application scenario

Soft reference the associated object. The JVM will recycle the object only when there is insufficient memory. This can be well used to solve the problem of oom, and this feature is very suitable for caching: such as web page caching, image caching, etc.

Now consider this scenario. In many applications, a large number of default images will appear, such as the default avatar of QQ, the default icon in the application, etc. These images will be used in many places.

If you read pictures every time, because the speed of reading files is slow, a large number of repeated reads will lead to performance degradation. Therefore, you can consider caching pictures and reading them directly from memory when necessary. However, due to the large memory space occupied by pictures, too many cached pictures will occupy more memory, which may be more prone to oom. At this point, soft references come in handy.

Note that the softreference object is used to hold soft references, but it is also a Java object. Therefore, after the soft accessible object is recycled, although the get () method of the softreference object returns null, the softreference object itself is not null. At this time, the softreference object no longer has the value of existence. An appropriate clearing mechanism is needed to avoid memory leakage caused by a large number of softreference objects.

ReferenceQueue is used to save these reference objects that need to be cleaned up. The soft reference can be used in conjunction with a reference queue. If the object referenced by the soft reference is recycled by the garbage collector, the Java virtual machine will add the soft reference to the associated reference queue.

The following is a simple cache class implemented with softreference:

public class SoftCache<T> {
    // 引用队列
    private ReferenceQueue<T> referenceQueue = new ReferenceQueue<>();
    // 保存软引用集合,在引用对象被回收后销毁
    private List<Reference<T>> list = new ArrayList<>();

    // 添加缓存对象
    public synchronized void add(T obj){
        // 构建软引用
        Reference<T> reference = new SoftReference<T>(obj,referenceQueue);
        // 加入列表中
        list.add(reference);
    }

    // 获取缓存对象
    public synchronized T get(int index){
        // 先对无效引用进行清理
        clear();
        if (index < 0 || list.size() < index){
            return null;
        }
        Reference<T> reference = list.get(index);
        return reference == null ? null : reference.get();
    }

    public int size(){
        return list.size();
    }

    @SuppressWarnings("unchecked")
    private void clear(){
        Reference<T> reference;
        while (null != (reference = (Reference<T>) referenceQueue.poll())){
            list.remove(reference);
        }
    }
}

Then test the cache class:

public class SoftCacheTest {
    private static int num = 0;

    public static void main(String[] args){
        SoftCache<OOMClass> softCache = new SoftCache<>();
        for (int i = 0; i < 40; i++) {
            softCache.add(new OOMClass("OOM Obj-" + ++num));
        }
        System.out.println(softCache.size());
        for (int i = 0; i < softCache.size(); i++) {
            OOMClass obj = softCache.get(i);
            System.out.println(obj == null ? "null" : obj.name);
        }
        System.out.println(softCache.size());
    }

    static class OOMClass{
        private String name;
        private int[] oom = new int[1024 * 100];// 100KB

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

Still use the previous virtual machine parameters:

-verbose:gc -Xms4m -Xmx4m -Xmn2m

Operation results:

[GC (Allocation Failure)  1017K->432K(3584K),0.0012236 secs]
[GC (Allocation Failure)  1117K->496K(3584K),0.0016875 secs]
[GC (Allocation Failure)  1347K->1229K(3584K),0.0015059 secs]
[GC (Allocation Failure)  2047K->2125K(3584K),0.0018090 secs]
[Full GC (Ergonomics)  2125K->1994K(3584K),0.0054759 secs]
[Full GC (Ergonomics)  2822K->2794K(3584K),0.0023167 secs]
[Full GC (Allocation Failure)  2794K->376K(3584K),0.0036056 secs]
[Full GC (Ergonomics)  2795K->2776K(3584K),0.0042365 secs]
[Full GC (Allocation Failure)  2776K->376K(3584K),0.0035122 secs]
[Full GC (Ergonomics)  2795K->2776K(3584K),0.0054760 secs]
[Full GC (Allocation Failure)  2776K->376K(3584K),0.0036965 secs]
[Full GC (Ergonomics)  2802K->2777K(3584K),0.0044513 secs]
[Full GC (Allocation Failure)  2777K->376K(3584K),0.0041400 secs]
[Full GC (Ergonomics)  2796K->2777K(3584K),0.0025255 secs]
[Full GC (Allocation Failure)  2777K->376K(3584K),0.0037690 secs]
[Full GC (Ergonomics)  2817K->2777K(3584K),0.0037759 secs]
[Full GC (Allocation Failure)  2777K->377K(3584K),0.0042416 secs]
缓存列表大小:40
OOM Obj-37
OOM Obj-38
OOM Obj-39
OOM Obj-40
缓存列表大小:4

It can be seen that after 40 soft reference objects are cached, if all of them are stored at one time, it is obvious that the memory size cannot be met. Therefore, in the process of continuously creating soft reference objects, GC continues to occur for garbage collection, and finally only 4 soft references are not cleaned up.

Comparison between strong reference and soft reference

There is no harm without comparison. Let's compare strong references with soft references:

public class Test {

    static class OOMClass{
        private int[] oom = new int[1024];
    }

    public static void main(String[] args) {
        testStrongReference();
        //testSoftReference();
    }

    public static void testStrongReference(){
        List<OOMClass> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(new OOMClass());
        }
    }

    public static void testSoftReference(){
        ReferenceQueue<OOMClass> referenceQueue = new ReferenceQueue<>();
        List<SoftReference> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            OOMClass oomClass = new OOMClass();
            list.add(new SoftReference(oomClass,referenceQueue));
            oomClass = null;
        }
    }
}

The results of running the teststrongreference method are as follows:

[GC (Allocation Failure)  1019K->384K(3584K),0.0033595 secs]
[GC (Allocation Failure)  1406K->856K(3584K),0.0013098 secs]
[GC (Allocation Failure)  1880K->1836K(3584K),0.0014382 secs]
[Full GC (Ergonomics)  1836K->1756K(3584K),0.0039761 secs]
[Full GC (Ergonomics)  2778K->2758K(3584K),0.0021269 secs]
[Full GC (Ergonomics)  2779K->2770K(3584K),0.0016329 secs]
[Full GC (Ergonomics)  2779K->2775K(3584K),0.0023157 secs]
[Full GC (Ergonomics)  2775K->2775K(3584K),0.0015927 secs]
[Full GC (Ergonomics)  3037K->3029K(3584K),0.0025071 secs]
[Full GC (Ergonomics)  3067K->3065K(3584K),0.0017529 secs]
[Full GC (Allocation Failure)  3065K->3047K(3584K),0.0033445 secs]
[Full GC (Ergonomics)  3068K->3059K(3584K),0.0016623 secs]
[Full GC (Ergonomics)  3070K->3068K(3584K),0.0028357 secs]
[Full GC (Allocation Failure)  3068K->3068K(3584K),0.0017616 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3352.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Heap dump file created [3855956 bytes in 0.017 secs]
[Full GC (Ergonomics)  3071K->376K(3584K),0.0032068 secs]
	at reference.Test$OOMClass.<init>(Test.java:11)
	at reference.Test.testStrongReference(Test.java:22)
	at reference.Test.main(Test.java:15)

Process finished with exit code 1

As you can see, oom is thrown soon because the Java heap space, that is, the heap memory is insufficient.

If you run the testsoftreference method, you will get the following results:

[GC (Allocation Failure)  1019K->464K(3584K),0.0019850 secs]
[GC (Allocation Failure)  1484K->844K(3584K),0.0015920 secs]
[GC (Allocation Failure)  1868K->1860K(3584K),0.0043236 secs]
[Full GC (Ergonomics)  1860K->1781K(3584K),0.0044581 secs]
[Full GC (Ergonomics)  2802K->2754K(3584K),0.0041726 secs]
[Full GC (Ergonomics)  2802K->2799K(3584K),0.0031293 secs]
[Full GC (Ergonomics)  3023K->3023K(3584K),0.0024830 secs]
[Full GC (Ergonomics)  3071K->3068K(3584K),0.0035025 secs]
[Full GC (Allocation Failure)  3068K->405K(3584K),0.0040672 secs]
[GC (Allocation Failure)  1512K->1567K(3584K),0.0011170 secs]
[Full GC (Ergonomics)  1567K->1496K(3584K),0.0048438 secs]

It can be seen that instead of throwing oom, GC is performed for many times. You can obviously see this:

[Full GC (Allocation Failure)  3068K->405K(3584K),0.0040672 secs]

When the memory is insufficient, a full GC is performed to reclaim most of the memory space, that is, most of the objects pointed to by the soft reference are reclaimed.

Summary

So far, this article has come to an end. Here we only briefly introduce the function and usage of soft reference. In fact, soft reference is not so good. Its use may have some fatal shortcomings. If you want to have a deeper understanding of the operation principle of soft reference, when soft reference is recycled and how it is recycled, you can check and browse the subsequent chapters.

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
分享
二维码
< <上一篇
下一篇>>