Skip to content

Commit 2ef0fd2

Browse files
jbachorikclaude
andcommitted
Add critical watermark + GC nudge to chaos MemoryGovernor
pace() now brakes harder (50ms vs 5ms) and update() fires a one-shot System.gc() above 0.93 cgroup / 0.90 heap, since plain throttling wasn't enough to stop a heap OOME in job 1861234807. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent d1f2a0f commit 2ef0fd2

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

ddprof-stresstest/src/chaos/java/com/datadoghq/profiler/chaos/MemoryGovernor.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
* treated as permanently low rather than guessed at.
3838
*
3939
* <p>Antagonists poll {@link #pace()} once per loop iteration — a volatile
40-
* read plus, only while throttled, a short sleep.
40+
* read plus, only while throttled, a short sleep. Above a second, higher
41+
* critical watermark, {@code pace()} sleeps much longer and the sampler
42+
* fires a one-off {@link System#gc()} to force reclaim before OOME hits —
43+
* plain throttling wasn't always enough to stop a heap OOME once several
44+
* antagonists kept allocating past the regular watermark (job 1861234807).
4145
*/
4246
final class MemoryGovernor {
4347

@@ -47,8 +51,13 @@ final class MemoryGovernor {
4751
// shed off-heap first), so back off earlier and require more slack.
4852
private static final double HEAP_HIGH_WATERMARK = 0.80;
4953
private static final double HEAP_LOW_WATERMARK = 0.65;
54+
// Critical tier: same signals, higher bar. Crossing this calls for a
55+
// harder brake than pace()'s regular 5ms sleep, plus a one-time GC nudge.
56+
private static final double CGROUP_CRITICAL_WATERMARK = 0.93;
57+
private static final double HEAP_CRITICAL_WATERMARK = 0.90;
5058
private static final long SAMPLE_INTERVAL_MS = 500;
5159
private static final long THROTTLE_SLEEP_MS = 5;
60+
private static final long CRITICAL_THROTTLE_SLEEP_MS = 50;
5261

5362
private static final Path[] USAGE_PATHS = {
5463
Paths.get("/sys/fs/cgroup/memory.current"),
@@ -60,6 +69,7 @@ final class MemoryGovernor {
6069
};
6170

6271
private static volatile boolean throttled;
72+
private static volatile boolean critical;
6373

6474
private MemoryGovernor() {
6575
}
@@ -81,7 +91,13 @@ static void start() {
8191

8292
/** Called from an antagonist's hot allocation loop. No-op unless throttled. */
8393
static void pace() {
84-
if (throttled) {
94+
if (critical) {
95+
try {
96+
Thread.sleep(CRITICAL_THROTTLE_SLEEP_MS);
97+
} catch (InterruptedException e) {
98+
Thread.currentThread().interrupt();
99+
}
100+
} else if (throttled) {
85101
try {
86102
Thread.sleep(THROTTLE_SLEEP_MS);
87103
} catch (InterruptedException e) {
@@ -119,18 +135,31 @@ private static void update(Path usagePath, long limitBytes) throws IOException {
119135

120136
boolean high = cgroupFraction >= CGROUP_HIGH_WATERMARK || heapFraction >= HEAP_HIGH_WATERMARK;
121137
boolean low = cgroupFraction <= CGROUP_LOW_WATERMARK && heapFraction <= HEAP_LOW_WATERMARK;
138+
boolean crit = cgroupFraction >= CGROUP_CRITICAL_WATERMARK || heapFraction >= HEAP_CRITICAL_WATERMARK;
122139
if (high && !throttled) {
123140
throttled = true;
124-
log(cgroupFraction, heapFraction, true);
141+
log(cgroupFraction, heapFraction, "throttling");
125142
} else if (low && throttled) {
126143
throttled = false;
127-
log(cgroupFraction, heapFraction, false);
144+
log(cgroupFraction, heapFraction, "released");
128145
}
129146
// else: in the dead zone between watermarks — keep current state.
147+
148+
if (crit && !critical) {
149+
critical = true;
150+
log(cgroupFraction, heapFraction, "critical");
151+
// One-shot nudge on the crossing, not every sample tick, so a
152+
// sustained critical state doesn't turn into a GC storm on top
153+
// of the memory pressure it's meant to relieve.
154+
System.gc();
155+
} else if (low && critical) {
156+
critical = false;
157+
log(cgroupFraction, heapFraction, "critical-released");
158+
}
130159
}
131160

132-
private static void log(double cgroupFraction, double heapFraction, boolean nowThrottled) {
133-
System.out.println("[chaos] memory-governor " + (nowThrottled ? "throttling" : "released")
161+
private static void log(double cgroupFraction, double heapFraction, String state) {
162+
System.out.println("[chaos] memory-governor " + state
134163
+ " cgroup=" + String.format("%.2f", cgroupFraction)
135164
+ " heap=" + String.format("%.2f", heapFraction));
136165
}

0 commit comments

Comments
 (0)