An overview of

This paper will be based on the definition of weak references, and then step by step through the use of a case of deep source analysis of its principle, so as to make readers deeply understand what is a weak reference, how to use weak references, what scenarios will use a weak reference, weak references can solve the problem of what, and what is its source code to achieve, which involve memory, Principles of Garbage Collection

Function:

JDK (JDK)

Weak references are mainly used in mapping that does not prevent its key or value from being reclaimed. Just post it in English, Weak references are for implementing canonicalizing mappings that do not prevent their keys (or values) from Being reclaimed)

Personal Understanding:

Weak references exist for garbage collection services. It refers to an object, but does not prevent it from being recycled. If a strong reference is used, the referenced object cannot be reclaimed as long as the reference exists. Weak references do not have this problem. When the garbage collector runs, if all references to an object are weak references, the object is collected

In-depth case analysis:

Ideally, we want to be able to recycle an object as soon as gc occurs when we are no longer using it. But sometimes, because of our carelessness, memory can overflow in the worst case. This is especially true when a map with a long lifetime holds many objects with a short lifetime. Take a look at the following example


public class StrongRefenceDemo {


    static Map<String, String> map;

    public static void main(String[] args) throws Exception {

        StrongRefenceDemo demo = new StrongRefenceDemo();
        demo.strongTest();
        System.out.println("Before GC occurs:" + map.size());
        System.out.println("Start notifying GC");
        // Note that the garbage collection is only done through the garbage collector and does not necessarily take place immediately
        System.gc();
        Thread.sleep(1000 * 5);
        System.out.println("After GC occurs:" + map.size());

    }


    /** * strong reference test */
    public void strongTest(a) {
        map = new HashMap<>();
        String key = new String("key");
        String value = new String("value");
        map.put(key, value);
        key = null;
        value = null; }}Copy the code

Output result after running:

Before gc occurs: 1 Notifying GC after GC occurs: 1Copy the code

As you can see from the output, even if we tell the JVM that we are no longer using the object by setting the key and value to null, the objects in the map are not collected by GC because the key and value are pointed to by a strongly referenced map. The garbage collector cannot recycle key and value objects. Map is defined as a static variable of Statis and is an object with a long usage life. There is a local key and value variable in the strongTest() method that ends its lifetime with the execution of the method, but the crude programmer forgot to remove it and the garbage collector can’t reclaim it. If there are many of these relatively short-lived objects, you can end up consuming all memory in the JVM.

But I’m curious here. If the key and value point to an object that is no longer needed after executing strongTest(), I might not be so good at calling remove to remove it. Do you want the garbage collector to decide if it’s ok to recycle? In fact, the answer is yes, this time is weak reference play, see the following program


public class WeakRefenceDemo {

    static Map<WeakReference<String>, String> map;

    public static void main(String[] args) throws Exception {

        WeakRefenceDemo demo = new WeakRefenceDemo();
        demo.weakTest();
        System.out.println("Before GC occurs:" + map.size());
        System.out.println("Start notifying GC");
        // Note that the garbage collection is only done through the garbage collector and does not necessarily take place immediately
        System.gc();
        Thread.sleep(1000 * 5);
        System.out.println("After GC occurs:" + map.size());


    }


    /** ** if reference test */
    public void weakTest(a) {
        map = new WeakHashMap<>();
        String key = new String("key");
        String value = new String("value");
        map.put(new WeakReference<>(key), value);
        key = null;
        value = null; }}Copy the code

Run the code above to print the result

Before gc occurs: 1 Notifying GC after GC occurs: 0Copy the code

From the output 0, we can determine that the garbage collection was successful. what? In the whole process, we just changed HashMap to WeakHashMap and key from String to WeakReference. Because strings have only weak references to them, they can be garbage collected. Isn’t it easy? It would be a waste if you stopped studying weak references at this point

WeakHashMap depth resolution

In the above program fragment, in fact, only key is set as a WeakReference new WeakReference<>(key), then normally only the corresponding memory of this key is reclaimed, because there is no call to remove, the value and entry inside will not be reclaimed. So why is the output size 0? Good question, we dig into WeakHashMap source code, we found a magic method expungeStaleEntries(). Before looking at the source code first parse the concept of reference queue: when weak references are recycled, the object will be put in the reference queue, which means that the object from the reference queue is recycled object, first explained here, enough to meet our source code analysis, next will do detailed analysis


    /** * Expunges stale entries from the table. */
    private void expungeStaleEntries(a) {
    // Retrieve a reclaimed object from the reference queue
        for(Object x; (x = queue.poll()) ! =null;) {synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                int i = indexFor(e.hash, table.length);

                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                // Set the value to NULL by iterating through the list to tell the garbage collector to recycle
                // Note that WeakHashMap and HashMap data structures are made up of arrays + linked lists
                while(p ! =null) {
                    Entry<K,V> next = p.next;
                    // When equal, it means that this is the object to be reclaimed
                    if (p == e) {
                    // Make the reclaimed object no longer referenced
                        if (prev == e)
                   
                            table[i] = next;
                        else
                        
                            prev.next = next;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        // This tells the garbage collection by setting value to null
                        e.value = null; // Help GC
                        size--;
                        break; } prev = p; p = next; }}}}Copy the code

From the above code snippet, the general meaning is to retrieve the recovered object from the reference queue, and then look for the object in WeakHashMap. After finding the object, the corresponding value is also set to NULL, and the corresponding entry is set to NULL, to tell GC to reclaim it. It can be seen from the source that expungeStaleEntries() is a method that is called when executing any WeakHashMap method

    /** * Returns the table after first expunging stale entries. */
    private Entry<K,V>[] getTable() {
    / / is invoked
        expungeStaleEntries();
        return table;
    }
    
        /** * Returns the number of key-value mappings in this map. * This result is a snapshot, and may not reflect unprocessed * entries that will be removed before next attempted access * because they are no longer  referenced. */
    public int size(a) {
        if (size == 0)
            return 0;
            / / is invoked
        expungeStaleEntries();
        return size;
    }
Copy the code

Now it’s clear why value is not set to a weak reference and can be reclaimed by calling the remove method without being explicit, right

Reference queue

From the above source code, we probably know the use of reference queue, so why use reference queue? If there were no reference queues, the above example would have required us to go through all the elements one by one. If there were a small number of elements, that would be fine. If there were a large number of elements, there would certainly be some performance issues. This problem can be easily solved with reference queues. We can see from WeakReference source code that there are two constructors, the second one is needed to pass in the reference queue

    public WeakReference(T referent) {
       super(referent);
   }
   
   public WeakReference(T referent, ReferenceQueue<? super T> q) {
       super(referent, q);
   }
Copy the code

Reference queue Hello Word

Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();

WeakReference weakReference1 = new WeakReference<>(referent);
WeakReference weakReference2 = new WeakReference<>(referent, referenceQueue);
Copy the code

Note the details in use: Since weakly referenced objects can be reclaimed during GC, we need to check whether they are null before using them to avoid null pointer exceptions

Object referent3 = weakReference2.get();
if(referent3 ! =null) {
    // GC hasn't removed the instance yet
} else {
    // GC has cleared the instance
}
Copy the code

conclusion

  1. Weak references are there for garbage collection
  2. An object can be reclaimed if it has only weak references to it
  3. Weak references are collected as soon as GC occurs, regardless of whether there is sufficient memory at the time
  4. If you specify a reference queue when creating weak references, the weak reference object will be placed in the reference queue when it is reclaimed
  5. For safe use, check whether the object is null each time to determine whether the object has been reclaimed to avoid null pointer exceptions

After watching two things

If you find this article inspiring, I’d like to invite you to do me two small favors:

  1. Like, so that more people can see this content (collection does not like, is a rogue -_-)
  2. Pay attention to the public number “interview BAT”, do not regularly share the original knowledge, the original is not easy, please support more (it also provides brush small program oh).

Jdk8 official documents resolve weak references