简体   繁体   中英

Threadlocal remove?

当我使用ThreadLocal时,我应该总是在完成后调用remove()或者当我set旧值时仍然会被替换,所以remove是多余的?

set always replaces the old value.

This is true for

  • Calendar.set() and Date.set()
  • BitSet.set()
  • List.set()
  • setters

You mean without remove it will not be GCed?

It will not be removed until the thread dies. It won't disappear on you without you calling remove()

Whether this is a memory leak or not depends on your program. You would have to create lots of threads with large thread local objects which you didn't need for some reason for it to matter. eg 1000 threads with a 1 KB object could waste up to 1 MB, but this suggest a design issue if you are doing this sort of thing.


The only place you might get a memory leak is.

for (int i = 0; ; i++) {
    // don't subclass Thread.
    new Thread() {
        // this is somewhat pointless as you are defining a ThreadLocal per thread.
        final ThreadLocal<Object> tlObject = new ThreadLocal<Object>() {
        };

        public void run() {
            tlObject.set(new byte[8 * 1024 * 1024]);
        }
    }.start();
    Thread.sleep(1);
    if (i % 1000 == 0) {
        System.gc();
        System.out.println(i);
    }
}

with -verbosegc prints.

[Full GC 213548K->49484K(3832192K), 0.0334194 secs]
39000
[GC 2786060K->82412K(3836864K), 0.0132035 secs]
[GC 2815569K->107052K(3836544K), 0.0212252 secs]
[GC 2836162K->131628K(3837824K), 0.0199268 secs]
[GC 2867613K->156204K(3837568K), 0.0209828 secs]
[GC 2886894K->180780K(3838272K), 0.0191244 secs]
[GC 2911942K->205356K(3838080K), 0.0187482 secs]
[GC 421535K->229932K(3838208K), 0.0192605 secs]
[Full GC 229932K->49484K(3838208K), 0.0344509 secs]
40000

Note: the size after a full GC is the same 49484K

In the above case you will have a ThreadLocal which refers to the Thread which refers to the ThreadLocal. However, as the Thread is dead it doesn't cause a memory leak becasue it becomes a regard object ie when A -> B and B -> A

I ran the above example in a loop for a couple of minutes and the GC levels moved around alot but the minimum size was still small.

Because ThreadLocal has Map of currentThread and value , Now if you don't remove the value in the thread which was using it then it will create a memory leak.

You should always call remove because ThreadLocal class puts values from the Thread Class defined by ThreadLocal.Values localValues; This will also cause to hold reference of Thread and associated objects.

From the source code of ThreadLocal

the value will be set to null and the underlying entry will still be present.

set : Sets the current thread's copy of this thread-local variable to the specified value.

Meaning whatever was in that memory location, will now be overwritten by what you passed through set

If the variable you're trying to remove will be always set in next executions of the thread, I wouldn't worry about removing it. set will overwrite its value.

But if you're setting that variable only in some circusmtances (for instance, when treating only a specific kind of requests), removing it might be convenient so that it doesn't stay around when, for instance, the thread is put back into the pool.

No you do not have to "always call remove() " instead of set()

If you fear memory leaks doing so, here is what the javadoc says

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

Thus not calling remove() will not prevent a thread-local instance to be properly garbage collected, and will not cause memory leaks by nature.

You can also take a look to ThreadLocal implementation, which use WeakReferences for this "implicit reference" mechanism

But beware of consistency with thread pools

Using set() method only with a thread pool, you might prefer to remove() a ThreadLocal instance, instead of overriding it in another "work unit" using the same thread. Because you might want to avoid the situation where, for some reason, the set method is not invoked, and your ThreadLocal remain attached to a context/treatment it does not belong.

I will make it simple: If you extend the ThreadLocal for any reason use remove() . On vanilla ThreadLocal use set(null) . Basically not using ThreadLocal.remove() on an extended ThreadLocal can lead to memory leaks (ClassLoader ones most likely)

If you need more details why, post a comment.

If the thread is done the threadLocalMap will be done with the thread. You do not need to remove it. But if the thread is used recycling you need to remove the value of threadLocalMap .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM