This is the 9th day of my participation in Gwen Challenge

Java concurrent programming – source code parsing ThreadLocal

Discriminating four references to Java, then I go?

ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal: ThreadLocal

However, improper use of ThreadLcoal can also lead to memory leaks and thread insecurity

1. Memory leak analysis of ThreadLcoal

Let’s start with an example that doesn’t use ThreadLcoal to analyze the memory footprint

Let’s first set the maximum heap memory to 256M!

public class ThreadLocalOOM {
    private static final int TASK_LOOP_SIZE = 500;

    final static ThreadPoolExecutor poolExecutor
            = new ThreadPoolExecutor(5.5.1,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<>());

    static class LocalVariable {
        private byte[] a = new byte[1024*1024*5];/* Array of 5M size */
    }

    ThreadLocal<LocalVariable> localVariable;

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < TASK_LOOP_SIZE; ++i) {
            poolExecutor.execute(new Runnable() {
                public void run(a) {
                    new LocalVariable();
                    System.out.println("use local varaible"); }}); Thread.sleep(100);
        }
        System.out.println("pool execute over"); }}Copy the code

Let’s run and play the Main method and then use the Java Visual Vm to walk around and see what heap memory looks like

We found that the heap size, up to about 25M, was exactly what we expected, but our code at work didn’t use ThreadLcoal yet

 public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < TASK_LOOP_SIZE; ++i) {
            poolExecutor.execute(new Runnable() {
                public void run(a) {
                    ThreadLocalOOM oom=new ThreadLocalOOM();
                    oom.localVariable=new ThreadLcoal<>();
                    oom.localVariable(new LocalVariable());
                    System.out.println("use local varaible"); }}); Thread.sleep(100);
        }
        System.out.println("pool execute over");
    }
Copy the code

Let’s take a look at memory usage again

Oh, wow, the memory is up to 150MB and even 200M

Is XDM taking up so much memory when we add ThreadLcoal?

There must have been a memory leak, so let’s add one more line of code and look at it

 public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < TASK_LOOP_SIZE; ++i) {
            poolExecutor.execute(new Runnable() {
                public void run(a) {
                    ThreadLocalOOM oom=new ThreadLocalOOM();
                    oom.localVariable=new ThreadLcoal<>();
                    oom.localVariable(new LocalVariable());
                    System.out.println("use local varaible"); oom.localVariable.remove(); }}); Thread.sleep(100);
        }
        System.out.println("pool execute over");
    }

Copy the code

Memory size fluctuations in nice runtime are back to before ThreadLocal was used.

Let’s go back to ThreadLcoal’s source code

The key of an Entry in a ThreadLcoal map is a weak reference. The key of an Entry in a ThreadLocalMap is a weak reference

To see how this works, draw a picture of XDM

We set the heap size to 256 at the beginning

ThreadLcoal is a weak reference, so when garbage is collected, the ThreadLocal is reclaimed, and the key is reclaimed. However, our current thread also references the Map, and the Map also references the Entry, so after the ThreadLocal is reclaimed, The value can’t be accessed through ThreadLocal, so that’s all that’s left

If we loop 500 times, the size of our memory leak should be larger than 200 MB, so why only 200 MB?

Let’s look at the set and GET methods of ThreadLcoalMap

It will clear the Entry with a null key, but this method is not executed every time the set is executed, which means that the collection is not timely, causing a certain amount of memory leak

Let’s go to the remove method

Oh roar! E. clean: delete Entry whose key is null;

What would happen if the key in our Entry had a strong reference? If it had a strong reference, we would have more memory leaks. If the reference to threadLocal was null, because it was a strong reference threadLocal, even though it was disconnected from the stack during garbage collection, But his Entry is still held by ThreadLocalMap, so a memory leak is bound to occur

2. The insecurity of ThreadLcoal

As always, the wrong way to use it can make the thread unsafe,

public class ThreadLocalUnsafe implements Runnable {

    public static Number number = new Number(0);

    public void run(a) {
        // Each thread count is incremented by one
        number.setNum(number.getNum()+1);
      // Store it in ThreadLocal
        value.set(number);
        SleepTools.ms(2);
        // Outputs the num value
        System.out.println(Thread.currentThread().getName()+"="+value.get().getNum());
    }

    public static ThreadLocal<Number> value = new ThreadLocal<Number>() {
    };

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(newThreadLocalUnsafe()).start(); }}private static class Number {
        public Number(int num) {
            this.num = num;
        }

        private int num;

        public int getNum(a) {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }

        @Override
        public String toString(a) {
            return "Number [num=" + num + "]"; }}}Copy the code

If XDM is not clear about references and objects, the stack and heap will make the same mistake. If XDM is not clear about references and objects, the set will be a reference, but the five threads will operate on the same object, so the Number object will be shared by five threads. There is a room for each thread, but instead of five rooms, there is a room for each thread.

3, summarize

ThreadLcoal source code analysis of the cause of memory leaks, so that we can use a great deal of time to avoid memory leaks, introduced a ThreadLocal unsafe use! If there are any mistakes, please point them out in the comments section! The big guys hit the button three times!