How MemorySafeLBQ Prevents OOM in Java Thread Pools – A Deep Dive
This article explores the design and implementation of MemoryLimitedLBQ and MemorySafeLBQ, two custom LinkedBlockingQueue variants that limit memory usage to avoid Out‑Of‑Memory errors in Java thread pools, comparing their mechanisms, instrumentation dependencies, and practical usage in open‑source projects.
MemorySafeLBQ
When browsing an open‑source PR on Apache Dubbo, I discovered a queue named MemorySafeLBQ that promises to be memory‑safe, sparking curiosity about its implementation.
The author explains why FixedThreadPool and SingleThreadPool are discouraged: their unbounded LinkedBlockingQueue can grow to Integer.MAX_VALUE, leading to OOM.
To mitigate this, two custom queues were introduced:
MemoryLimitedLBQ – limits the total memory a queue can occupy by tracking each element’s size using Instrumentation.getObjectSize.
MemorySafeLBQ – limits queue insertion based on the JVM’s remaining free memory, using ManagementFactory.getMemoryMXBean instead of instrumentation.
MemoryLimitedLBQ
This queue extends LinkedBlockingQueue and adds a memoryLimiter that maintains memoryLimit, a LongAdder memory for current usage, and several locks. The put method calls memoryLimiter.acquireInterruptibly, which checks the object’s size and blocks if adding it would exceed memoryLimit.
A bug was identified where the local variable sum was used instead of the up‑to‑date memory.sum(), causing a potential dead‑loop. The fix replaces sum with memory.sum().
MemorySafeLBQ
Unlike its predecessor, MemorySafeLBQ does not rely on Instrumentation. It defines maxFreeMemory (default 256 MiB) and checks the JVM’s free memory via MemoryMXBean. If the remaining memory falls below the threshold, insertion is rejected.
The queue overrides put and offer to call hasRemainedMemory before delegating to the superclass, simplifying usage.
Comparison and Usage
Both queues can be copied into any project without framework dependencies. MemoryLimitedLBQ offers fine‑grained control per element but requires instrumentation; MemorySafeLBQ provides a simpler, JVM‑wide safeguard without extra agents.
Examples and test cases are linked in the original PRs, and the author suggests extending the concept to other data structures like local caches or dynamically adjusting queue parameters.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
