Java multithreading -- Introduction to ThreadLocal and memory leakage

Posted by TheFilmGod on Sun, 16 Jan 2022 15:19:37 +0100

ThreadLocal

ThreadLocal is called Thread local variable. It creates a copy of the variable in each Thread. Each Thread accesses and modifies the copy of the variable in this Thread, but the variables between threads cannot access each other. ThreadLocal is not a Thread.

ThreadLocal has four methods:

ThreadLocal action

ThreadLocal allows the thread to monopolize resources and store them inside the thread to avoid the decline of CPU throughput caused by thread congestion.

The same threadLocal is used, but the variables of each thread are independent and invisible to other threads. There is no need for each thread to new an object, which reduces the memory overhead.

Detailed explanation of ThreadLocal method

public class ThreadLocalTest {

    public static void main(String[] args) {

        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    threadLocal.set(1);
                    System.out.println(threadLocal.get());
                }finally {
                    threadLocal.remove();
                }
            }
        }, "Thread1").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set(2);
                System.out.println(threadLocal.get());
            }
        }, "Thread2").start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(threadLocal.get());
            }
        }, "Thread3").start();
    }

}
1
2
null

You can see that each thread uses a reference to the same threadLocal, but variables between threads cannot access each other.

How does ThreadLocal create a replica? (how to maintain variables)

Each Thread contains a ThreadLocalMap. The key of ThreadLocalMap is the ThreadLocal object and the value is the exclusive data.

Each Thread maintains a ThreadLocalMap mapping table. The key of this mapping table is the ThreadLocal instance itself, and the value is the Object that really needs to be stored.

In other words, ThreadLocal itself does not store values, it is just used as a key to let the thread obtain values from ThreadLocalMap.

ThreadLocal memory leak problem

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k); //Continue to dig deeply. key is a weak reference and points to ThreadLcoal object k with a weak reference
        value = v;
    }
}

ThreadLocalMap uses the weak reference of ThreadLocal as the Key, and the weakly referenced objects will be recycled during GC.

ThreadLocalMap uses the weak reference of ThreadLocal as the key. If a ThreadLocal does not have an external strong reference to reference it, the ThreadLocal is bound to be recycled during system GC (why it is recycled, as described below). Then entries with null key will appear in ThreadLocalMap, so there is no way to access the value s of these entries with null key.

If the current thread does not end for a long time, the values of these null key entries will always have a strong reference chain: thread ref - > thread - > threalocal map - > Entry - > value can never be recycled, resulting in memory leakage.

The above summary is that the key is null and is recycled, but the value is still there. It can only be recycled after judgment during set and get.

So why can't value be set to weak reference?

If the vaule is designed as a weak reference, you may get null and meaningless.

Why use weak references instead of strong references?

Strong reference:

Object object= new Object();

Strong references are used the most. In any case, as long as the strong reference relationship still exists, the garbage collector will never recycle the referenced objects.

object =null

For an ordinary object, if there is no other reference relationship, it can be collected as garbage as long as it exceeds the scope of the reference or explicitly assigns the corresponding (strong) reference to null. Of course, the specific collection time depends on the garbage collection strategy.

Strong references are one of the main causes of JAVA memory leaks. When the memory space is insufficient, the Java virtual opportunity throws OutOfMemoryError to make the program terminate abnormally. However, note that throwing an error will not recycle these strongly referenced objects

Weak reference:

String str = new String("123");
WeakReference<String> weakReference = new WeakReference<>(str);
str = null;

During a garbage collection cycle, the jvm finds an object with only weak references, and will reclaim its memory regardless of whether the current memory space is sufficient or not.

Therefore, weak references have a shorter life cycle than strong references.

There are four References: https://www.cnblogs.com/yanl55555/p/13365397.html

Understand from the previous figure:

Look at the figure. When the main thread ends, the stack frame is destroyed, and the strong reference ThreadLocal is gone. Look at the red part again,

If it is a strong reference, the k reference of an entry in the ThreadLocalMap of the thread also points to the ThreadLocal object. As a result, the ThreadLocal object pointed to by k and the object pointed to by v cannot be recycled by the jvm virtual machine gc, resulting in memory leakage.

If it is a weak reference, the ThreadLocal object can be recycled at the end of execution, because only the K weak reference of entry points to it. After ThreadLocal is recycled, k points to null, but v still has a value and there is a risk of memory leakage. There is no guarantee that there will be no memory leakage.

So why should ThreadLocal use weak references instead of strong references?

The conclusion is to reduce the risk of serious memory leakage.

As mentioned above, the key is a weak reference. When the key is null, the value is not null, so the value cannot be recycled, causing a memory leak.

Weak references also have the risk of memory leakage, and strong references are more. When using thread pool, the number of customized threads is not standardized. If strong reference is used, the risk of memory leakage is higher.

How to prevent memory leakage?

The value of entry mentioned above also has the risk of memory leakage.

ThreadLocal has methods: when calling get,set or remove methods, it will try to delete the entry with null key, which can free the memory occupied by the value object.

The correct example of the above demo should be:

new Thread(new Runnable() {
    @Override
    public void run() {
        try{
            threadLocal.set(1);
            System.out.println(threadLocal.get());
        }finally {
            threadLocal.remove(); //remove after use to prevent memory leakage
        }
    }
}, "Thread1").start();

Core of remove method:

 private void remove(ThreadLocal<?> key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i); //Call elimination method
                    return;
                }
            }
        }

private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    // expunge entry at staleSlot
    tab[staleSlot].value = null;   //Assign the value of vaule to null
    tab[staleSlot] = null; 
    size--;

There are also set methods:

// If key not found, put new entry in stale slot
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);

reference resources:

https://blog.csdn.net/qq_33404395/article/details/82356344

https://www.cnblogs.com/shen-qian/p/12108655.html

Topics: Java Back-end Multithreading ThreadLocal