Description
There is a critical Unintended ThreadLocal (UTL) memory leak in the ConstructorInstrumenter class. The lastObject ThreadLocal is set but never cleared, leading to severe memory retention, especially in thread-pool environments.
Location
File: com.google.monitoring.runtime.instrumentation.ConstructorInstrumenter
Method: invokeSamplers(Object o)
Root Cause
In the invokeSamplers method, the newly allocated object is bound to the current thread:
lastObject.set(o);
However, there is no corresponding lastObject.remove() nor a try-finally block. If a registered sampler throws a RuntimeException, or even during normal method return, the object remains permanently strongly-referenced by the thread.
Impact
In high-throughput architectures using thread pools (e.g., APM agents in Flink workers), pooled threads will permanently hold onto these potentially large, short-lived object graphs. Over time, this causes severe memory spikes, premature promotion to the Old Gen, and catastrophic Full GC pauses.
Proposed Fix
Wrap the sampler execution in a try-finally block to ensure the ThreadLocal is safely cleared:
lastObject.set(o);
try {
Class<?> currentClass = o.getClass();
// ... existing traversal and sampler logic ...
} finally {
lastObject.remove();
}
Description
There is a critical Unintended ThreadLocal (UTL) memory leak in the
ConstructorInstrumenterclass. ThelastObjectThreadLocal is set but never cleared, leading to severe memory retention, especially in thread-pool environments.Location
File:
com.google.monitoring.runtime.instrumentation.ConstructorInstrumenterMethod:
invokeSamplers(Object o)Root Cause
In the
invokeSamplersmethod, the newly allocated object is bound to the current thread:lastObject.set(o);However, there is no corresponding
lastObject.remove()nor atry-finallyblock. If a registered sampler throws aRuntimeException, or even during normal method return, the object remains permanently strongly-referenced by the thread.Impact
In high-throughput architectures using thread pools (e.g., APM agents in Flink workers), pooled threads will permanently hold onto these potentially large, short-lived object graphs. Over time, this causes severe memory spikes, premature promotion to the Old Gen, and catastrophic Full GC pauses.
Proposed Fix
Wrap the sampler execution in a
try-finallyblock to ensure the ThreadLocal is safely cleared: