Part 3 · 2 chapters · ~12 min
Memory, Leaks and GC
RSS against managed heap, heap dumps and snapshots per runtime (V8, JVM with Eclipse MAT, Go heap profiles, Python tracemalloc), off-heap and native memory, allocator fragmentation, GC logs and pause analysis, and container OOM kills.
7
Finding the kind of memory first
code
# Linux: break RSS down
pmap -x <pid> | sort -k3 -n | tail # largest mappings
grep -E 'Rss|Anon|File' /proc/<pid>/smaps_rollup
# JVM: native memory tracking
java -XX:NativeMemoryTracking=summary ...; jcmd <pid> VM.native_memory summary
jcmd <pid> GC.heap_dump /tmp/heap.hprof # open in Eclipse MAT: dominator tree, leak suspects
# Go
curl -s localhost:6060/debug/pprof/heap > heap.pb.gz && go tool pprof -sample_index=inuse_space heap.pb.gz
# Python
import tracemalloc; tracemalloc.start(25); ...; tracemalloc.take_snapshot().statistics('traceback')[:10]WHERE DID THE MEMORY GO?
RSS is the sum of several kinds of memory; each has its own tool
swipe the figure sideways, or tap expand for full screen
1/5
RSS first
The container is killed on RSS, not on heap size. Compare RSS with the runtime's heap metrics: if they diverge, the growth is outside the managed heap.
OOM kills on RSScompare RSS with heap metrics
8
GC logs and pauses
| runtime | GC visibility | pause-related levers |
|---|---|---|
| JVM | -Xlog:gc*, JFR | G1 (default, pause targets), ZGC and Shenandoah (sub-millisecond pauses), heap sizing |
| Go | GODEBUG=gctrace=1 | GOGC (heap growth before GC), GOMEMLIMIT (soft limit, Go 1.19+) |
| Node | --trace-gc | old space size, semi-space size, allocation rate (Node course part 1) |
| Python | gc.set_debug, gc.callbacks | reference counting frees most objects; the cycle collector handles cycles; gc.freeze() for forked workers |
A common Go production setting: GOMEMLIMIT at about 90% of the container limit so the GC works harder before the OOM killer acts. The equivalent idea in Node is sizing old space below the limit.