Install
openclaw skills install memory-leak-hunterDetect and diagnose memory leaks in applications. Analyze heap snapshots, track memory growth over time, identify leaked objects (event listeners, closures,...
openclaw skills install memory-leak-hunterFind what's eating your application's memory. Analyze heap growth patterns, compare snapshots, identify retained objects (event listeners, closures, timers, detached DOM nodes, unclosed connections), and generate specific fixes — not just "you have a leak" but exactly where and why.
Use when: "memory leak", "memory keeps growing", "OOM killed", "heap dump analysis", "why is memory usage so high", "application memory keeps increasing", or when memory usage doesn't stabilize under steady traffic.
detect — Find Memory Leaks# Node.js — track heap over time
node -e "
const used = () => process.memoryUsage();
setInterval(() => {
const m = used();
console.log(JSON.stringify({
rss: (m.rss / 1048576).toFixed(1) + ' MB',
heap: (m.heapUsed / 1048576).toFixed(1) + ' MB',
external: (m.external / 1048576).toFixed(1) + ' MB'
}));
}, 5000);
" &
# Send load for 2 minutes, then observe if memory returns to baseline
# Python — track memory
python3 -c "
import tracemalloc, time
tracemalloc.start()
# ... run your code ...
snapshot = tracemalloc.take_snapshot()
for stat in snapshot.statistics('lineno')[:10]:
print(stat)
"
# Any process — watch RSS over time
watch -n 5 "ps -o pid,rss,vsz,comm -p \$(pgrep -f 'your-app')"
# Container memory
kubectl top pods --sort-by=memory 2>/dev/null
docker stats --no-stream 2>/dev/null
# Take heap snapshots at intervals
# Method 1: via inspector protocol
node --inspect=9229 app.js &
# Send signal to take snapshot
kill -USR2 $(pgrep -f app.js)
# Method 2: programmatic
node -e "
const v8 = require('v8');
const fs = require('fs');
const snap1 = v8.writeHeapSnapshot();
console.log('Snapshot 1:', snap1);
// ... do work ...
const snap2 = v8.writeHeapSnapshot();
console.log('Snapshot 2:', snap2);
"
Compare two snapshots — objects that grew between them are leak candidates:
Event listeners not removed:
rg "addEventListener|\.on\(|\.once\(" --type ts --type js -g '!node_modules' 2>/dev/null | \
while read line; do
file=$(echo "$line" | cut -d: -f1)
# Check if there's a corresponding removeEventListener
rg "removeEventListener|\.off\(|\.removeListener\(" "$file" 2>/dev/null | wc -l
done
Closures retaining scope:
# Find closures in loops or repeated callbacks that capture large objects
rg -U "setInterval|setTimeout|\.on\(.*=>\s*\{" --type ts --type js -g '!node_modules' 2>/dev/null | head -20
Unclosed resources:
# Find open without corresponding close
rg "\.open\(|createConnection|createPool|createClient" --type ts --type js --type py -g '!node_modules' 2>/dev/null
# Check for .close() or .end() or .destroy() in same files
Detached DOM nodes (browsers):
# Find DOM manipulation without cleanup
rg "createElement|appendChild|insertBefore" --type ts --type js -g '!node_modules' 2>/dev/null
# Check for corresponding removeChild or cleanup
Growing collections:
# Find arrays/maps/sets that only push/add, never clear/delete
rg "\.push\(|\.set\(|\.add\(" --type ts --type js -g '!node_modules' -g '!*.test.*' 2>/dev/null | head -20
# Memory Leak Analysis
## Evidence
- RSS grew from 120 MB to 850 MB over 6 hours under steady load
- Heap used grew from 80 MB to 620 MB
- No corresponding increase in active connections or requests
- Memory does NOT decrease after traffic stops → confirmed leak
## Leaked Objects (from heap diff)
1. **String** — +45,000 instances, +120 MB retained
Source: `logger.ts:23` — error messages appended to in-memory buffer without rotation
Fix: Add max buffer size or use streaming logger
2. **EventEmitter listeners** — +2,300 instances
Source: `websocket.ts:67` — `socket.on('message')` handlers added on reconnect, never removed
Fix: Call `socket.removeAllListeners('message')` before re-adding
3. **Promise** — +8,000 unresolved
Source: `api-client.ts:45` — timeout promises created but never rejected on disconnect
Fix: Add AbortController with timeout cleanup
## Recommendations
1. Add `--max-old-space-size=512` to Node.js flags as safety limit
2. Implement log rotation (max 1000 entries in memory buffer)
3. Add connection cleanup in WebSocket reconnection handler
4. Monitor `process.memoryUsage()` and alert at 70% of limit
profile — Continuous Memory ProfilingSet up lightweight continuous memory monitoring:
fix — Generate Fix for Common PatternsFor each identified leak pattern, provide the exact code fix: