TL;DR : You cannot count on the value of a ThreadLocal
being garbage collected when the ThreadLocal
object is no longer referenced. You have to call ThreadLocal.remove
or cause the thread to terminate
(Thanks to @Lii)
Detailed answer:
from that it seems that objects referenced by a ThreadLocal
variable are garbage collected only when thread dies.
That is an over-simplification. What it actually says is two things:
The value of the variable won't be garbage collected while the thread is alive (hasn't terminated), AND the ThreadLocal
object is strongly reachable.
The value will be subject to normal garbage collection rules when the thread terminates.
There is an important third case where the thread is still live but the ThreadLocal
is no longer strongly reachable. That is not covered by the javadoc. Thus, the GC behavior in that case is unspecified, and could potentially be different across different Java implementations.
In fact, for OpenJDK Java 6 through OpenJDK Java 17 (and other implementations derived from those code-bases) the actual behavior is rather complicated. The values of a thread's thread-locals are held in a ThreadLocalMap
object. The comments say this:
ThreadLocalMap
is a customized hash map suitable only for maintaining thread local values. [...] To help deal with very large and long-lived usages, the hash table entries use WeakReferences
for keys. However, since reference queues are not used, stale entries are guaranteed to be removed only when the table starts running out of space.
If you look at the code, stale map entries (with broken WeakReferences
) may also be removed in other circumstances. If stale entry is encountered in a get, set, insert or remove operation on the map, the corresponding value is nulled. In some cases, the code does a partial scan heuristic, but the only situation where we can guarantee that all stale map entries are removed is when the hash table is resized (grows).
So ...
Then during context undeploy application classloader becomes a subject for garbage collection, but thread is from a thread pool so it does not die. Will object b
be subject for garbage collection?
The best we can say is that it may be ... depending on how the application manages other thread locals the thread in question.
So yes, stale thread-local map entries could be a storage leak if you redeploy a webapp, unless the web container destroys and recreates all of the request threads in the thread pool. (You would hope that a web container would / could do that, but AFAIK it is not specified.)
The other alternative is to have your webapp's servlets always clean up after themselves by calling ThreadLocal.remove
on each one on completion (successful or otherwise) of each request.