First, we started with two questions

First, when will WeakReferent be added to the linked list? What determines whether it should be added?

Second, how to update the object’s new address to the weak reference in the case of both weak reference and strong reference.

Let me illustrate how these two issues are handled by the JVM. First of all, as mentioned in last class, when GC scans a weak reference, it puts the weak reference in the linked list first, but does not move the referenced object. That’s one situation.

In the second case, there are both strong references and weak references. As shown in the figure below, black dotted lines represent weak references and solid lines represent strong references.

If A is scanned first and then C is checked during A GC scan, then both A and C will be moved into survivor space. So you do a forward operation.

Then, when B is scanned, it is found that the object referenced by B, namely C, has been forward to the new survivor space. In this case, B is also moved to the new space, and C’s new address is added to B. As shown in the figure below, B is not added to the discovered list. B’s reference to C is no different from a strong reference.

So what if B scans first? When B sees that C is not in the new Survivor space, the JVM puts B into the list first and moves B into survivor (note that B is always alive), leaving C untouched. Then when A is scanned, it is still treated as A normal reference, and both A and C are moved to the new space. However, this way, when the GC ends, B is pointing to the wrong object. As shown below:

So, we have to check this situation again after the GC ends.

The method is to iterate through the Reference list to see if the referent object of each Reference survives, that is, if it is forwarded to a new survivor. When we introduced copy GC, we said that the markOop of an object that has been forward points to the new address. In the preceding figure, the forwarding pointer to C’ points to C, and we use this pointer to point B to C. Of course, remember, B doesn’t need to be processed anymore, so we should remove B from the list. After this treatment, in the new space, it looks like this:

This solves the two problems left over from last time.

As a final step, after being added to the linked list, the JVM is responsible for setting the WeakReference object’s reference to NULL, and then the ReferenceHandler thread handles the list.

All right. WeakReference If you still don’t understand this, ask me directly. I feel like I’ve made myself pretty clear.

SoftReference

The treatment of SoftReference is the same as that of WeakReference. The difference is that it simply adds a little more judgment to whether to add Reference to the list.

In the hotspot/SRC/share/vm/memory/referenceProcessor CPP discover_reference this method, you can see this condition:

if (rt == REF_SOFT) {
    // For soft refs we can decide now if these are not
    // current candidates for clearing, in which case we
    // can mark through them now, rather than delaying that
    // to the reference-processing phase. Since all current
    // time-stamp policies advance the soft-ref clock only
    // at a major collection cycle, this is always currently
    // accurate.
    if (!_current_soft_ref_policy->should_clear_reference(obj, _soft_ref_timestamp_clock)) {
      return false;
    }    
  }
Copy the code

The implementation of should_clear_reference looks like this:

// The oop passed in is the SoftReference object, and not
// the object the SoftReference points to.
bool LRUMaxHeapPolicy::should_clear_reference(oop p,
                                             jlong timestamp_clock) {
  jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p);
  assert(interval >= 0, "Sanity check");

  // The interval will be zero if the ref was accessed since the last scavenge/gc.
  if(interval <= _max_interval) {
    return false;
  }

  return true;
}
Copy the code

If the survival time of a SoftReference is greater than _max_interval, WeakReference is treated like WeakReference; if not, SoftReference is treated as a normal strong reference.

Here are several timestamps, defined in the JDK.

Public class SoftReference<T> extends Reference<T> {// Updated by the JVM, which records the time when the last GC occurred. static private long clock; // Each call to get is updated to record when the current Reference was last accessed. 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; } // the main difference with super.get is that every call to get updates the last GC time, i.e. // clock, to timestamp. public T get() { T o = super.get(); if (o ! = null && this.timestamp ! = clock) this.timestamp = clock; return o; }}Copy the code

All right. With these variables in mind, let’s go back to this statement:

jlong interval = timestamp_clock - java_lang_ref_SoftReference::timestamp(p);
Copy the code

The Java code corresponding to this statement is ref.clock-ref.timestamp, but it is written in the JVM to access the Java Object directly.

One last question, where is _max_interval set?

// Capture state (of-the-VM) information needed to evaluate the policy
void LRUMaxHeapPolicy::setup() {
  size_t max_heap = MaxHeapSize;
  max_heap -= Universe::get_heap_used_at_last_gc();
  max_heap /= M;

  _max_interval = max_heap * SoftRefLRUPolicyMSPerMB;
  assert(_max_interval >= 0,"Sanity check");
}
Copy the code

Calculate how much space is left in the heap after the last GC and multiply it to _max_interval. That is, the smaller the max_heap is, the smaller the _max_interval is, and the more likely SoftReference is to be reclaimed. Most of you have probably seen this quote: **SoftReference is only reclaimed if you run out of memory space. ** But no one can say when the heap is out of memory. I showed you the code here. Hotspot has a very clear definition of running out of memory.

In addition, this code shows you a JVM parameter: SoftRefLRUPolicyMSPerMB. The smaller this parameter is, the more likely SoftReference is to be reclaimed as soon as possible.