A probe into the threadlocal memory leak of Java concurrent programming

Source: Internet
Author: User

Improper use of ThreadLocal can lead to memory leaks, what causes a memory leak?

Let's start by looking at an example with the following code:

/** * Created by Cong on 2018/7/14.*/ Public classThreadlocaloutofmemorytest {Static classlocalvariable {PrivateLong[] A =Newlong[1024x768*1024x768]; }    //(1)FinalStaticThreadpoolexecutor Poolexecutor =NewThreadpoolexecutor (6, 6,1, Timeunit.minutes,NewLinkedblockingqueue<>()); //(2)FinalStaticThreadlocal<localvariable> localvariable =NewThreadlocal<localvariable>();  Public Static voidMain (string[] args) throws Interruptedexception {//(3)         for(inti =0; I < -; ++i) {Poolexecutor.execute (NewRunnable () { Public voidrun () {//(4)Localvariable.Set(Newlocalvariable ()); //(5)System. out. println ("Use local varaible");//Localvariable.remove ();                }            }); Thread.Sleep ( +); }        //(6)System. out. println ("Pool Execute over"); }}

Code (1) creates a thread pool with a core number of threads and a maximum thread count of 6, which guarantees that there are 6 threads running at any time in the thread pool.

Code (2) creates a ThreadLocal variable whose generic parameter is localvariable,localvariable inside is a Long array.

The Code (3) Places 50 tasks into the thread pool.

The code (4) sets the localvariable variable for the current thread, which is to put the new localvariable variable into the threadlocals variable of the current thread.

Because there is no shutdown or Shutdownnow method to call the thread pool, the user thread inside the thread pool does not exit, and the JVM process does not exit.

After running, we immediately open the Jconsole monitoring heap memory changes, such as:

Next, let's Open the Localvariable.remove () comment and then run, observing the heap memory changes as follows:

From the first run results, when the main thread is dormant, the process takes up about 75M of memory, and the second run after opening the Localvariable.remove () comment takes up about 25M of memory, but does not write Localvariable.remove () When the memory has leaked, the following analysis of the cause of the leak, as follows:

The first run of code, after setting the thread's localvariable variable does not call the method, resulting in the thread pool inside the localVariable.remove() 5 thread of the threadlocals variable inside the new LocalVariable() instance is not released, although the thread pool inside the task is completed, but the thread pool inside the 5 threads will persist until the JVM exits. It is important to note that because Localvariable is declared static, localvariable is not recycled, although the threadlocalmap inside the thread is a weak reference to localvariable. The code that runs the result two does not have a memory leak because the thread is cleaning up after setting the localvariable variable, even if the localVariable.remove() method is called.

Next we want to know clearly the root cause of the memory leak, then we will go into the source to see.

We know that threadlocal is just a tool class, where the variable is stored in the threadlocals variable of the thread, Threadlocals is a threadlocalmap type, we first list Threadlocalmap class diagram structure , the class diagram structure is as follows:

If the Threadlocalmap is an Entry array, Entry inherits the value from the weakreference,entry inside to hold values passed through the ThreadLocal's set method, then the ThreadLocal object Where did you store it?

Here's a look at the Entry constructor, as follows:

Entry (threadlocal<?> K, Object v) {    super (k);     = V;}

Then let's look at the constructor of entry's parent class WeakReference super (k), as follows:

 Public WeakReference (T referent) {   super (referent);}

Then we look at the constructor of WeakReference's parent class reference Super (Referent), as follows:

Reference (T referent) {   thisnull);}

Then we look at the other constructor of WeakReference's parent class reference this (referent, null), as follows:

Reference (T referent, referencequeue<? super t> queue) {   this. referent= referent;    thisnull)? ReferenceQueue.NULL:queue;}

K is passed to the WeakReference constructor, that is, the key inside the Threadlocalmap is a weak reference to the ThreadLocal object, specifically the referent variable refers to the ThreadLocal object, value The value passed for the set method that specifically calls ThreadLocal.

When a thread calls the set method of ThreadLocal, the threadlocalmap of the current thread will hold a record, the key of the record is a ThreadLocal reference, and value is the value set.

But consider if the ThreadLocal variable has no other strong dependencies, and the current thread still exists, because the key inside the thread's threadlocalmap is a weak dependency, the current thread's threadlocalmap inside the ThreadLocal A weak reference to a variable is recycled in the GC, but a memory leak is caused by the value of the Threadlocalmap, where the key is null but the value is not NULL entry.

In fact, in the ThreadLocal set and get and remove methods have some time to clean up these key null entry, but these cleanup does not have to happen, the following is a brief explanation of the Threadlocalmap remove side The removal process, remove the source code, as follows:

Private voidRemove (threadlocal<?>key) {  //(1) Calculate the position of the table array where the current threadlocal variable is located, try using the Quick location methodentry[] Tab =table; intLen =tab.length; inti = Key.threadlocalhashcode & (len-1); //(2) The use of loops here is to prevent the fast location failure after the variable table array   for(Entry e = tab[i]; E! =NULL; E = Tab[i =Nextindex (i, Len)]) {      //(3) Find      if(E.Get() ==key) {          //(4) Find the Clear method that calls WeakReference to clear the weak reference to Threadlocale.clear (); //(5) Cleanup of key null elementsexpungestaleentry (i); return; }   }} Private intExpungestaleentry (intStaleslot) {entry[] tab=table; intLen =tab.length; //(6) Remove the reference to valueTab[staleslot].value =NULL; Tab[staleslot]=NULL; Size--;
Entry e; inti; for(i = Nextindex (Staleslot, Len); (e = Tab[i])! =NULL; i =Nextindex (i, Len)) {ThreadLocal<?> k = e.Get(); //(7) If key is null, the reference to value is removed. if(k = =NULL) {E.value=NULL; Tab[i]=NULL; Size--; } Else { inth = k.threadlocalhashcode & (Len-1); if(H! =i) {Tab[i]=NULL; while(Tab[h]! =NULL) H=Nextindex (H, Len); TAB[H]=e; } } } returni; }

The code (4) calls the clear method of Entry, which is actually called the clear method of the parent class WeakReference, to remove the weak reference to ThreadLocal.

The code (6) removes the reference to value, where the information for the current ThreadLocal object inside the front thread is cleared.

Code (7) Start with the subscript of the current element to see if the other elements inside the table array have a key of NULL, and then clean up. The condition of a loop exit is that it encounters a null element in the table. So here we know that the null element behind the Entry is not cleaned by the key null element.

Summarize:

1.THREADLOCALMAP internal Entry key uses a weak reference to the ThreadLocal object, which is an improvement to avoid memory leaks, because if it is a strong reference, then even if there is no reference to the ThreadLocal object elsewhere, The ThreadLocal object in Threadlocalmap is still not recycled, and if it is a weak reference, then ThreadLocal references are recycled.

2. However, if the value is still not recoverable, there will be a entry entry in Threadlocalmap that has key null but value is not NULL, although Threadlocalmap provides set,get,remove The method will clean up these Entry items at some time, but this is not timely, it is not executed every time, so in some cases there will be a memory leak, so after the use of the Remove method is the best way to resolve the memory leak.

3. The thread pool set the ThreadLocal variable must remember to clean up in time, because the thread pool inside the core thread is always there, if not clean, then the thread pool core thread of the threadlocals variable will always hold the ThreadLocal variable.

A probe into the threadlocal memory leak of Java concurrent programming

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.