Type references in Java

Strong weak virtual

Strong reference

Stack memory points to heap memory

public class MGCTest {
    public static void main(String[] args) {
        M m = new M();
        m = null; System.gc(); }}Copy the code

When m of stack memory points to new M() of heap memory, the new m () is reclaimed when m=null is triggered by GC.

Soft references

The first sample

/** * create by yanghongxing on 2020/5/19 11:48 PM */
public class SoftReferenceM {
    public static void main(String[] args) throws InterruptedException {
        SoftReference<byte[]> m = new SoftReference<>(new byte[1024 * 1024 * 10]);
        System.out.println(m.get());
        System.gc();
        System.out.println(m.get());
        SoftReference<byte[]> n = new SoftReference<>(new byte[1024 * 1024 * 11]);
        System.out.println(m.get());
      
      	ThreadLocal<M> mThreadLocal = new ThreadLocal<>();
        mThreadLocal.set(newM()); mThreadLocal.get(); }}Copy the code

I create a weak reference. In the reference relationship, a SoftReference object is created in the first step, a Byte object is created in the second step, a SoftReference object is referenced in the third step, and M is referenced in the third step. We set a JVM parameter -xmx20m to the maximum heap memory of 20m. The output is:

[B@372f7a8d
[B@372f7a8d
null
Copy the code

Because we set the heap to a maximum of 20 MB, we created a 10 MB byte array the first time and an 11 MB byte array the second time. When the heap ran out of memory on the second time, we reclaimed the previous 10 MB array.

A weak reference

public class WeakReferenceM {
    public static void main(String[] args) {
        WeakReference<M> m = new WeakReference<>(newM()); System.out.println(m.get()); System.gc(); System.out.println(m.get()); }}Copy the code

Output result:

com.example.demo.quote.M@372f7a8d
null
finalize
Copy the code

The weak reference garbage collector sees it and collects it. ThreadLocal is a local thread object. This object exists whenever a thread exists. For example, in spring’s Transactional annotation, the same connection object must be retrieved from different methods to ensure a transaction. The connection object is stored in ThreadLocal. Let’s look at the source code for ThreadLocal.

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if(map ! =null)
            map.set(this, value);
        else
            createMap(t, value);
    }
Copy the code

We get the current thread object, we get a map, and we put the current object in that map, and this is the ThreadLocal object

 /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
Copy the code

T.htreadlocals, t is a member variable of the Thread object. Let’s look at the source of the set method

/**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal
        key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for(Entry e = tab[i]; e ! =null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<? > k = e.get();if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if(! cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }Copy the code

This constructs an Entry object, which can be viewed as a row of map data, a key-value pair. Take a look at the source code for Entry.

static class Entry extends WeakReference<ThreadLocal<? >>{
            /** The value associated with this ThreadLocal. */Object value; Entry(ThreadLocal<? > k, Object v) {super(k); value = v; }}Copy the code

The Entry object actually inherits the WeakReference object. So a typical use of weak references is in ThreadLocal.

MThreadLocal = new ThreadLocal<>() The key of the map refers to the ThreadLocal object. This object uses weak references, which are used to prevent memory leaks. Why is ThreadLocal so prone to memory leaks when weak references are used here? The key is a weak reference, but the value is not, so the record is still in the map, so it is prone to memory leaks. To prevent memory leaks, we remove the record as soon as the ThreadLocal is used.

Phantom reference

public class PhontamReferenceM {
    private static ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
    private static List<byte[]> LIST = new ArrayList();


    public static void main(String[] args) {
        PhantomReference<M> m = new PhantomReference<>(new M(), QUEUE);
        new Thread(() -> {
            while (true) {
                LIST.add(new byte[1024 * 1024]);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("0" + m.get());
            }
        }).start();
        new Thread(() -> {
            while (true) {
                Reference<? extends M> poll = QUEUE.poll();
                if (Objects.nonNull(poll)) {
                    System.out.println("1"+ poll); } } }).start(); }}Copy the code

The output is:

0null
1java.lang.ref.PhantomReference@b148489
0null
0null
0null
0null
Copy the code

The main purpose of virtual references is to manage out-of-heap memory. For example, niO operations may have some out-of-heap memory to improve efficiency. Out-of-heap memory cannot be directly reclaimed by GC, but when objects are reclaimed, Queue can detect it and clean up out-of-heap memory.