Debugging and Fixing the ConcurrentLinkedQueue Memory Leak Bug in JDK8
This article explains the root cause of a memory‑leak bug in JDK8's ConcurrentLinkedQueue, demonstrates how to reproduce and visualize the issue with custom CLQ implementations, analyzes the removal algorithm, and shares practical debugging tips including a notorious IDEA debug pitfall and its resolution.
The author begins with a personal anecdote before diving into a technical deep‑dive on a ConcurrentLinkedQueue (CLQ) memory‑leak bug that appeared in JDK8, originally highlighted in a previous article about a 60‑hour monitoring run.
After summarizing the bug’s symptoms, the article shows how to create a custom CLQ class ( whyConcurrentLinkedQueue , abbreviated WhyCLQ ) by copying the JDK8 source and renaming it, then runs a test case that triggers an Unsafe ‑related exception at line 931.
The exception originates from getUnsafe() detecting a non‑null class loader, which violates the expectation that core JDK classes are loaded by the bootstrap class loader. The author revisits the parent‑delegation model to explain why the custom class is loaded by AppClassLoader and thus fails.
To work around the exception, the article introduces a reflective hack using Unsafe to bypass the loader check, allowing the custom CLQ to function. It then adds logging methods to print the linked‑list structure before and after each operation, using a helper sortName method to shorten object identifiers.
With the instrumented CLQ, the author compares JDK7 (pre‑fix) and JDK8 (post‑fix) removal behavior, showing how JDK8’s remove(obj) correctly nullifies node items and prevents the list from growing, whereas JDK7 leaves dangling null items that cause memory growth and performance degradation.
The article proceeds to a detailed walkthrough of JDK8’s remove source code, highlighting the two key modification points (lines ~490 and ~505) where logging can be inserted to observe the queue’s state.
Finally, the author shares a practical IDEA debugging pitfall: the IDE implicitly calls toString() on the queue during debugging, which triggers the iterator’s first() method and unintentionally modifies the head node, leading to inconsistent behavior and NPEs. The solution is to disable the two IDEA settings that invoke toString() automatically.
Overall, the article provides a comprehensive guide to reproducing, visualizing, and fixing the CLQ memory‑leak bug, while offering general advice on debugging non‑blocking concurrent data structures.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.