Backend Development 3 min read

How ThreadLocal Can Cause Memory Leaks in Java Multithreading

This article explains how ThreadLocal creates per‑thread variable copies, demonstrates its usage with sample Java code, illustrates the internal storage structure, and reveals why improper handling can lead to memory leaks when thread‑local values persist in thread pools.

Lobster Programming
Lobster Programming
Lobster Programming
How ThreadLocal Can Cause Memory Leaks in Java Multithreading

ThreadLocal provides each thread with its own isolated copy of a variable, ensuring that values set in one thread do not affect others.

The following example shows how to create two ThreadLocal instances, assign values inside a new thread, and retrieve them:

<code>public static void main(String[] args) {
    ThreadLocal<String> threadLocal = new ThreadLocal<>();
    ThreadLocal<String> threadLocal2 = new ThreadLocal<>();

    Thread thread = new Thread(() -> {
        threadLocal.set("longxia");
        threadLocal2.set("biancheng");

        String content = threadLocal.get();
        System.out.println(Thread.currentThread().getName() + " 内部的002: " + content);

        String content2 = threadLocal2.get();
        System.out.println(Thread.currentThread().getName() + " 内部的003: " + content2);
    }, "t1");
    thread.start();
}
</code>

The internal storage of ThreadLocal is a ThreadLocalMap held inside each thread. The diagram below illustrates the map’s structure, where each thread has its own map entries.

Each entry in ThreadLocalMap is an Entry object that extends WeakReference to the ThreadLocal key. The source of ThreadLocalMap looks like this:

<code>static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    // ...
}
</code>

Although the key (the ThreadLocal instance) may be reclaimed by the garbage collector, the associated value remains reachable as long as the thread itself lives. In thread‑pool scenarios, core threads are reused and not destroyed, so the value persists indefinitely, leading to a memory leak.

JavaconcurrencyMemory Leakmultithreadingthreadlocal
Lobster Programming
Written by

Lobster Programming

Sharing insights on technical analysis and exchange, making life better through technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.