A few days ago when I was in CodeReview, I saw a code using WeakHashMap, and then I talked about WeakReference, and then I talked about Java four reference types.

Recall that the last time YOU learned about Java’s four strong and weak reference types, you were preparing for an interview. Usually use not much, suddenly unexpectedly can’t think clearly of their difference, only remember their intensity successively decreasing.

Came down and looked at this aspect of the article, today to make them clear.

The difference between the four citations

The difference between the four references is how they are handled during GC. If an object’s GC Root is reachable, strong references are not collected, soft references are collected when out of memory, and weak references are collected on the object’s first GC.

If GC Root is unreachable, then whatever references are recycled

Virtual references are special, equal to no reference, which does not affect the life cycle of the object, but can receive a system notification when the object is reclaimed by the collector.

Here are some examples of how each of the four references behaves when dealing with GC and their common uses. Set the JVM parameters:

-Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails
Copy the code

Strong reference

This is the quote we use most often. As long as the object is reachable to GC Root at GC time, it will not be reclaimed. If the JVM runs out of memory, throw OOM. For example, this code raises an OutOfMemoryError:

public static void main(String[] args) {
    List<Object> list = new LinkedList<>();
    for (int i = 0; i < 21; i++) {
        list.add(new byte[1024 * 1024]);
    }
} Copy the code

Soft references

Soft references, when GC, if GC Root is reachable, if memory is sufficient, will not be collected; If the memory is insufficient, it will be reclaimed. Change the above example to soft reference and it will not be included in OOM:

public static void main(String[] args) {
    List<Object> list = new LinkedList<>();
    for (int i = 0; i < 21; i++) {
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024]);
        list.add(softReference);
 } } Copy the code

Let’s change the program to print the difference after GC:

public static void main(String[] args) {
    List<SoftReference<byte[]>> list = new LinkedList<>();
    for (int i = 0; i < 21; i++) {
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024]);
        list.add(softReference);
 System.out.println("Before the gc." + softReference.get());  }  System.gc();  for (SoftReference<byte[]> softReference : list) {  System.out.println("After the gc:" + softReference.get());  } } Copy the code

You will find that the printed logs have values before GC, and some of them will be null after GC, indicating that they have been collected.

If we set the maximum heap size to 20M, if we change the cycle times to 15, we will find that the printed logs are not null after GC. However, the -verbose: gc-xx :+PrintGCDetails argument shows that the JVM did a few gc’s, but it didn’t reclaim because there was enough memory.

public static void main(String[] args) {
    List<SoftReference<byte[]>> list = new LinkedList<>();
    for (int i = 0; i < 15; i++) {
        SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024]);
        list.add(softReference);
 System.out.println("Before the gc." + softReference.get());  }  System.gc();  for (SoftReference<byte[]> softReference : list) {  System.out.println("After the gc:" + softReference.get());  } } Copy the code

So here comes a common use for soft references: caching. Especially if you want the cache to last longer.

A weak reference

Soft references, which are collected whenever the object is GC.

If you change the above code to soft reference, you will find that the printed logs are all null after GC.

public static void main(String[] args) {
    List<WeakReference<byte[]>> list = new LinkedList<>();
    for (int i = 0; i < 15; i++) {
        WeakReference<byte[]> weakReference = new WeakReference<>(new byte[1024 * 1024]);
        list.add(weakReference);
 System.out.println("Before the gc." + weakReference.get());  }  System.gc();  for (WeakReference<byte[]> weakReference : list) {  System.out.println("After the gc:" + weakReference.get());  } } Copy the code

Weak references are also good for caching, but because they are recycled whenever GC occurs, they have a much shorter lifetime than soft references and are often used for very temporary caching.

We know that WeakHashMap internally manages entries through weak references. Its key is a “weak key”, so its corresponding key-value pair is also removed from the Map during GC.

Tomcat has a ConcurrentCache, using WeakHashMap, combined with ConcurrentHashMap, to achieve a thread-safe cache, interested students can study the source code, the code is very concise, with all the comments, only 59 lines.

Entry in the static internal class ThreadLocalMap of ThreadLocal is an inherited class of WeakReference.

A weak reference lets ThreadLocalMap know if a ThreadLocal object is invalid. Once that object is invalid, that is, garbage, the data in the Map it controls is no longer useful because the outside world can no longer access it, so it decides to erase the associated value object in the Map. A reference to an Entry object to ensure that the Map is always as small as possible.

Phantom reference

Virtual references are designed a little differently from the above three in that they do not affect the GC, but rather in order to receive a system notification when an object is GC.

So how was it notified? A virtual reference must work with the ReferenceQueue. When the GC is about to reclaim an object, if it finds that it has a virtual reference, it adds the virtual reference to the ReferenceQueue associated with it before recycling.

So how does NIO use virtual references to manage memory?

DirectBuffer requests a chunk of memory directly from outside the Java heap, which is not directly managed by the JVM GC, meaning that it is not directly manipulated in the GC algorithm. This memory is GC because DirectBuffer objects in the Java heap are cleaned up by a notification mechanism after GC.

DirectBuffer has a Cleaner inside. This Cleaner is a subclass of PhantomReference. When the DirectBuffer object is reclaimed, it is notified to PhantomReference. The ReferenceHandler then calls the tryHandlePending() method for pending processing. If pending is not null, the DirectBuffer has been recycled, and Cleaner clean() can be called for recycling.

The above method code in the Reference class, interested students can go to see the source code of that method.

conclusion

Those are the differences between the four references in Java. In general, strong references we all know that virtual references are rarely used. The difference between a soft reference and a weak reference lies in the timing of the collection: when a soft reference is collected, it is found that there is not enough memory, and when a weak reference is collected, it takes only one GC.

About the author

Wechat public number: made up a process

Personal website: https://yasinshaw.com

Pseudonym Yasin, a programmer with depth, attitude and warmth. After work to share programming technology and life, if you like my article, you can easily “follow” the public account, also welcome to “forward” to share with your friends ~

Reply “interview” or “study” on the official account to receive corresponding resources oh ~

The public,