Does MDC Cause Memory Leaks? Java ThreadLocal Investigation and Results

This article investigates whether the Mapped Diagnostic Context (MDC) in Java logging can lead to memory leaks by analyzing its ThreadLocal implementation, running multiple thread‑pool scenarios, and comparing the findings with classic ThreadLocal leak cases.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Does MDC Cause Memory Leaks? Java ThreadLocal Investigation and Results

In the context of high availability, the author references a popular saying: "others may die, we should not, and we should not cause our own death." Recently, Log4j2's remote code execution vulnerability has highlighted the risk of being killed by others, prompting a deeper look at logging components.

The author refactors a custom unified logging component, focusing on the safety of org.slf4j.MDC and potential memory leaks caused by improper ThreadLocal usage. After source code study and tests, confidence is restored.

Verification

The source code of MDC was examined, tracing its use of ThreadLocal. No container storing ThreadLocal references was found that could cause unbounded growth. A practical test was then performed.

public static void main(String[] args) {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 200, 1, TimeUnit.DAYS, new LinkedTransferQueue<>());
    for (long i = 0; i < Long.MAX_VALUE; i++) {
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    MDC.put("k", Thread.currentThread().getName());
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Scenario 1

Maximum thread count set to 10,000 caused memory to grow continuously, and the test machine could not keep up.

Scenario 2

Core pool size 10, max 20, with MDC.clear() at the end of each task. Memory stabilized at about 2095 MB.

Scenario 3

Same as Scenario 2 but without MDC.clear(). Memory stabilized at about 2045 MB.

Scenario 4

Core pool size 1, max 2, with MDC.clear(). Memory stabilized at about 2116 MB.

Scenario 5

Same as Scenario 4 without MDC.clear(). Memory stabilized at about 2109 MB.

Scenario 6

Core pool size 2, max 4. Memory stabilized at about 2120 MB.

Across all scenarios, whether MDC.clear() was called or not, memory usage eventually stabilized, indicating no memory leak.

Theoretical Analysis

ThreadLocal memory leaks are a classic interview topic. A test that creates a new ThreadLocal in a tight loop demonstrates unbounded growth because each ThreadLocal instance remains referenced in the thread's internal ThreadLocalMap.

public void test() throws Exception {
    for (int i = 0; i < 100; i++) {
        ThreadLocal threadLocal = new ThreadLocal();
        threadLocal.set("t1" + i);
        Thread t = Thread.currentThread();
        log.info(t);
    }
}

The source of the leak is that the ThreadLocal key stays in the thread's map, preventing garbage collection and eventually causing OutOfMemoryError.

Why MDC Does Not Leak

MDC stores data via a static MDCAdapter implementation. In common logging frameworks like Logback, the adapter holds a single ThreadLocal<Map<String, String>>. Each thread creates at most one ThreadLocal, so the map size per thread remains bounded, preventing leaks.

public class LogbackMDCAdapter implements MDCAdapter {
    final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal();
    public void put(String key, String val) {
        if (key == null) throw new IllegalArgumentException("key cannot be null");
        Map<String, String> oldMap = this.copyOnThreadLocal.get();
        if (oldMap != null) {
            oldMap.put(key, val);
        } else {
            Map<String, String> newMap = new HashMap<>();
            newMap.put(key, val);
            this.copyOnThreadLocal.set(newMap);
        }
    }
}

Thus, MDC does not suffer from the same unbounded growth as raw ThreadLocal usage.

Conclusion

Never assume safety; always prove it with tests and source analysis.

High availability means avoiding self‑inflicted failures and protecting against external attacks.

MDC, built on a single ThreadLocal per thread, does not cause memory leaks in typical usage.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendloggingmemory leakThreadLocalmdc
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

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.