google / google/allocation-instrumenter

[Bug] Memory Leak via Unintended ThreadLocal (UTL) Misuse in ConstructorInstrumenter

Open
#60 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
492
Forks
89
PR merge metrics
No merged PRs in 30d

Description

### 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:

```java
lastObject.set(o);
try {
Class currentClass = o.getClass();
// ... existing traversal and sampler logic ...
} finally {
lastObject.remove();
}

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.