-
Notifications
You must be signed in to change notification settings - Fork 660
Description
Describe the bug
I identified a High-Water Mark memory leak in quickfix.Message.
The class uses a private static final ThreadLocal stringContexts to cache a StringBuilder for message string generation. In the toString() method, the buffer is reset using stringBuilder.setLength(0) in the finally block.
The Issue: StringBuilder.setLength(0) resets the logical character count but does not release the underlying internal char[] array capacity. If a thread processes a single exceptionally large message (e.g., 10MB), the StringBuilder attached to that thread will expand to 10MB and never shrink back, effectively holding that memory indefinitely even when processing subsequent small messages.
Source Code Location:
// quickfix/Message.java
// 1. ThreadLocal Definition
private static final ThreadLocal stringContexts = new ThreadLocal() { ... };
// 2. Usage in toString()
public String toString() {
Context context = stringContexts.get();
StringBuilder stringBuilder = context.stringBuilder;
try {
// ... append large data ...
return stringBuilder.toString();
} finally {
// ISSUE: Only resets count, keeps the expanded capacity forever
stringBuilder.setLength(0);
}
}
To Reproduce
-
Initialize a thread pool (e.g., Fix Application threads).
-
Construct a quickfix.Message with a large payload (e.g., 20MB body).
-
Call message.toString() on a thread. (The ThreadLocal buffer expands to ~20MB).
-
Discard the message and let the thread process small heartbeat messages.
-
Observation: Inspect the Heap Dump. The thread's ThreadLocalMap still holds a StringBuilder with a ~20MB char[] backing array. Multiplied by the number of threads, this causes significant heap waste.
Expected behavior
The cached StringBuilder should manage its capacity. Ideally, in the finally block, check if the stringBuilder.capacity() exceeds a reasonable threshold (e.g., 8KB or 64KB). If it does, either call trimToSize() or replace it with a new, smaller instance to release the excess memory.
system information:
OS: All
Java version: All
QFJ Version: (Based on analysis of provided Message.java)