You must know the type of java reference — detailed explanation of softreference source code

definition

Softreference is a soft reference, and its referenced objects will be recycled when memory is insufficient. The object to which only the soft reference points is called a soft reachable object.

explain

The garbage collector will recycle soft reachable objects when the memory is insufficient and the memory is still insufficient after a garbage collection. Before the virtual machine throws oom, it will ensure that all soft references to soft reachable objects have been cleared.

If there is enough memory, there is no specific time for recycling soft references, so soft reference objects may survive for a long time when there is enough memory.

The JVM will decide whether to recycle soft reachable objects according to the current memory situation, but as long as there is a strong reference to the referent, the referent will not be cleaned up. Therefore, softreference is suitable for implementing memory sensitive caches. The recycling strategy of soft references will be slightly different in different JVM implementations.

Generally speaking, the softreference object will be put into its registered reference queue (if it is registered at the time of creation) only after the garbage collector collects its internal references.

Soft reference objects,which are cleared at the discretion of the garbage collector in response to memory demand. 

In other words, when soft references are recycled is ultimately determined by the virtual machine itself, so the recycling methods of soft references will be different for different virtual machines.

翻译错误 Invalid Access Limit

public class SoftReference<T> extends Reference<T> {
    /**
     * 由垃圾回收器负责更新的时间戳
     */
    static private long clock;

    /**
     * 在get方法调用时更新的时间戳,当虚拟机选择软引用进行清理时,可能会参考这个字段。
     */
    private long timestamp;

    public SoftReference(T referent) {
        super(referent);
        this.timestamp = clock;
    }

    public SoftReference(T referent,ReferenceQueue<? super T> q) {
        super(referent,q);
        this.timestamp = clock;
    }

    /**
     * 返回引用指向的对象,如果referent已经被程序或者垃圾回收器清理,则返回null。
     */
    public T get() {
        T o = super.get();
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }
}

There are few internal codes in the softreference class. There are two member variables. Clock is a static variable, which is the timestamp updated by the garbage collector. During JVM initialization, the variable clock will be initialized. At the same time, when a GC occurs in the JVM, the value of clock will also be updated, so clock will record the time point of the last GC.

Timestamp is the timestamp updated during creation and update. It is updated to the value of clock. The garbage collector may refer to timestamp when recycling soft reference objects.

Softreference class has two constructors, one is not to pass reference queue, and the other is to pass reference queue. When creating, the timestamp will be updated and assigned to the value of clock. The get method has no operation, but simply calls super Get() and update the timestamp when the return value is not null.

When are soft references recycled

As mentioned earlier, soft references will be recycled when memory is insufficient, but they will not be recycled all at once, but a certain recycling strategy will be used.

The following describes the most commonly used virtual machine hotspot. The following is the description in the Oracle documentation:

The default value is 1000 ms per megabyte,which means that a soft reference will survive (after the last strong reference to the object has been collected) for 1 second for each megabyte of free space in the heap

The default life cycle is 1000ms / MB, for example:

Assuming that the heap memory is 512MB and the available memory is 400MB, we create an object a, create a cache object cache referencing a with soft reference, and another object B references object a. At this time, since B holds a strong reference to a, object a is strongly reachable and will not be collected by the garbage collector.

If B is deleted, a has only one soft reference cache to reference it. If a is not strongly referenced again within 400s, it will be deleted after timeout.

Here is a chestnut that controls soft references:

public class SoftRefTest {
    public static class A{
    }
    public static class B{
        private A strongRef;
 
        public void setStrongRef(A ref) {
            this.strongRef = ref;
        }
    }
    public static SoftReference<A> cache;
 
    public static void main(String[] args) throws InterruptedException{
        //用一个A类实例的软引用初始化cache对象
        SoftRefTest.A instanceA = new SoftRefTest.A();
        cache = new SoftReference<SoftRefTest.A>(instanceA);
        instanceA = null;
        // instanceA 现在是软可达状态,并且会在之后的某个时间被垃圾回收器回收
        Thread.sleep(10000);
 
        ...
        SoftRefTest.B instanceB = new SoftRefTest.B();
        //由于cache仅持有instanceA的软引用,所以无法保证instanceA仍然存活
        instanceA = cache.get();
        if (instanceA == null){
            instanceA = new SoftRefTest.A();
            cache = new SoftReference<SoftRefTest.A>(instanceA);
        }
        instanceB.setStrongRef(instanceA);
        instanceA = null;
        // instanceA现在与cache对象存在软引用并且与B对象存在强引用,所以它不会被垃圾回收器回收
 
        ...
    }
}

However, it should be noted that the objects associated with soft reference objects will be automatically recycled by the garbage collector, but the soft reference object itself is also an object. These created soft references will not be automatically recycled by the garbage collector. Therefore, in the chestnuts described in the previous article, soft references will not be released.

Therefore, you still need to clean them manually, otherwise it will also lead to oom. Here is also a small Chestnut:

public class SoftReferenceTest{

    public static class MyBigObject{
        int[] data = new int[128];
    }

    public static int CACHE_INITIAL_CAPACITY = 100_000;
    // 静态集合保存软引用,会导致这些软引用对象本身无法被垃圾回收器回收
    public static Set<SoftReference<MyBigObject>> cache = new HashSet<>(CACHE_INITIAL_CAPACITY);

    public static void main(String[] args) {
        for (int i = 0; i < 100_000; i++) {
            MyBigObject obj = new MyBigObject();
            cache.add(new SoftReference<>(obj));
            if (i%10_000 == 0){
                System.out.println("size of cache:" + cache.size());
            }
        }
        System.out.println("End");
    }
}

The virtual machine parameters used are:

-Xms4m -Xmx4m -Xmn2m

The output is as follows:

size of cache:1
size of cache:10001
size of cache:20001
size of cache:30001
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

Finally, oom is thrown, but the reason here is not Java heap space, but GC overhead limit exceeded. The reason why this error is thrown is that the virtual machine has been recycling soft references. The recycling speed is too fast, the CPU occupied is too large (more than 98%), and the memory recycled each time is too small (less than 2%).

For this, the appropriate processing method is to register a reference queue and remove the soft reference objects in the reference queue from the cache after each cycle.

public class SoftReferenceTest{

    public static int removedSoftRefs = 0;

    public static class MyBigObject{
        int[] data = new int[128];
    }

    public static int CACHE_INITIAL_CAPACITY = 100_000;
    // 静态集合保存软引用,会导致这些软引用对象本身无法被垃圾回收器回收
    public static Set<SoftReference<MyBigObject>> cache = new HashSet<>(CACHE_INITIAL_CAPACITY);
    public static ReferenceQueue<MyBigObject> referenceQueue = new ReferenceQueue<>();

    public static void main(String[] args) {
        for (int i = 0; i < 100_000; i++) {
            MyBigObject obj = new MyBigObject();
            cache.add(new SoftReference<>(obj,referenceQueue));
            clearUselessReferences();
        }
        System.out.println("End,removed soft references=" + removedSoftRefs);
    }

    public static void clearUselessReferences() {
        Reference<? extends MyBigObject> ref = referenceQueue.poll();
        while (ref != null) {
            if (cache.remove(ref)) {
                removedSoftRefs++;
            }
            ref = referenceQueue.poll();
        }
    }
}

Using the same virtual machine configuration, the output is as follows:

End,removed soft references=97319

Processing of soft references in hotspot virtual machine

As far as the hotspot virtual machine is concerned, the common recycling strategy is the LRU policy based on the current heap size. The value of clock is used to subtract timestamp. The difference is the time when the soft reference is idle. If it is idle enough, it is considered recyclable.

bool LRUCurrentHeapPolicy::should_clear_reference(oop p,jlong timestamp_clock) {
  jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p);
  assert(interval >= 0,"Sanity check");

  if(interval <= _max_interval) {
    return false;
  }

  return true;
}

Here is timestamp_ Clock is the value of clock in softreference, that is, the last GC time. java_ lang_ ref_ Softreference:: timestamp (P) can obtain the value of timestamp in the reference.

Well, this is long enough_ max_ How is the interval calculated?

void LRUCurrentHeapPolicy::setup() {
  _max_interval = (Universe::get_heap_free_at_last_gc() / M) * SoftRefLRUPolicyMSPerMB;
  assert(_max_interval >= 0,"Sanity check");
}

Softreflrupolicymspermb is 1000 by default, so it can be seen that the recovery time is related to the remaining space after the last GC. The larger the available space is_ max_ The greater the interval.

If the available space of the heap is still large after GC, the softreference object can be stored in the heap for a long time without being recycled. Conversely, if only a small amount of memory is available after GC, the softreference object will be recycled quickly.

Softreference will affect garbage collection to a certain extent. If the corresponding referent in the soft reachable object still fails to meet the release conditions for many times, it will stay in the old age of the heap and occupy a large part of the space. Before the JVM does not throw outofmemoryerror, it may lead to frequent full GC, which will have a certain impact on the performance.

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