
Memory Leak Detection
- 507 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
memory-leak-detection is a Claude Code skill that helps developers investigate rising heap usage, orphaned event listeners, and retained objects in long-running clients and services.
About
memory-leak-detection is a debugging skill from useful-ai-prompts for tracing memory growth in long-running clients and backend services before release or after performance complaints. It guides investigation of rising heap usage, orphaned event listeners, and retained object graphs that cause gradual memory bloat. Developers reach for memory-leak-detection when production or staging processes show climbing RSS or heap metrics and standard profiling has not isolated the retaining path.
- Heap and allocation profiling
- Listener and closure audits
- Reproduction scenarios
- Fix prioritization
- Regression test ideas
Memory Leak Detection by the numbers
- 507 all-time installs (skills.sh)
- Ranked #87 of 596 Debugging skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill memory-leak-detectionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 507 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you debug memory leaks in production services?
Investigate rising heap usage, orphaned listeners, and retained objects before release or after performance complaints in long-running clients and services.
Who is it for?
Developers debugging gradual memory growth in long-running services or client apps ahead of release or after user performance reports.
Skip if: One-time CPU spikes, network latency issues, or greenfield projects with no observed memory growth symptoms.
When should I use this skill?
User reports rising heap usage, orphaned listeners, retained objects, or memory bloat in long-running clients or services.
What you get
Identified leak sources, listener cleanup fixes, retained object reports, and stabilized heap usage profiles.
- Leak source identification
- Listener cleanup recommendations
Files
Memory Leak Detection
Table of Contents
Overview
Identify and fix memory leaks to prevent out-of-memory crashes and optimize application performance.
When to Use
- Memory usage growing over time
- Out of memory (OOM) errors
- Performance degradation
- Container restarts
- High memory consumption
Quick Start
Minimal working example:
import v8 from "v8";
import fs from "fs";
class MemoryProfiler {
takeSnapshot(filename: string): void {
const snapshot = v8.writeHeapSnapshot(filename);
console.log(`Heap snapshot saved to ${snapshot}`);
}
getMemoryUsage(): NodeJS.MemoryUsage {
return process.memoryUsage();
}
formatMemory(bytes: number): string {
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}
printMemoryUsage(): void {
const usage = this.getMemoryUsage();
console.log("Memory Usage:");
console.log(` RSS: ${this.formatMemory(usage.rss)}`);
console.log(` Heap Total: ${this.formatMemory(usage.heapTotal)}`);
console.log(` Heap Used: ${this.formatMemory(usage.heapUsed)}`);
console.log(` External: ${this.formatMemory(usage.external)}`);
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Node.js Heap Snapshots | Node.js Heap Snapshots |
| Memory Leak Detection Middleware | Memory Leak Detection Middleware |
| Common Memory Leak Patterns | Common Memory Leak Patterns |
| Python Memory Profiling | Python Memory Profiling |
| WeakMap/WeakRef for Cache | WeakMap/WeakRef for Cache |
| Memory Monitoring in Production | Memory Monitoring in Production |
Best Practices
✅ DO
- Remove event listeners when done
- Clear timers and intervals
- Use WeakMap/WeakRef for caches
- Limit cache sizes
- Monitor memory in production
- Profile regularly
- Clean up after tests
❌ DON'T
- Create circular references
- Hold references to large objects unnecessarily
- Forget to clean up resources
- Ignore memory growth
- Skip WeakMap for object caches
Common Memory Leak Patterns
Common Memory Leak Patterns
// BAD: Event listener leak
class BadComponent {
constructor() {
window.addEventListener("resize", this.handleResize);
}
handleResize = () => {
// Handler logic
};
// Missing cleanup!
}
// GOOD: Proper cleanup
class GoodComponent {
constructor() {
window.addEventListener("resize", this.handleResize);
}
handleResize = () => {
// Handler logic
};
destroy() {
window.removeEventListener("resize", this.handleResize);
}
}
// BAD: Timer leak
function badFunction() {
setInterval(() => {
doSomething();
}, 1000);
// Interval never cleared!
}
// GOOD: Clear timer
function goodFunction() {
const intervalId = setInterval(() => {
doSomething();
}, 1000);
return () => clearInterval(intervalId);
}
// BAD: Closure leak
function createClosure() {
const largeData = new Array(1000000).fill("data");
return function () {
// largeData kept in memory even if unused
console.log("closure");
};
}
// GOOD: Don't capture unnecessary data
function createClosure() {
const needed = "small data";
return function () {
console.log(needed);
};
}
// BAD: Global variable accumulation
let cache = [];
function addToCache(item: any) {
cache.push(item);
// Cache grows indefinitely!
}
// GOOD: Bounded cache
class BoundedCache {
private cache: any[] = [];
private maxSize = 1000;
add(item: any) {
this.cache.push(item);
if (this.cache.length > this.maxSize) {
this.cache.shift();
}
}
}Memory Leak Detection Middleware
Memory Leak Detection Middleware
class LeakDetector {
private samples: number[] = [];
private maxSamples = 10;
private threshold = 1.5; // 50% growth
checkForLeak(): boolean {
const usage = process.memoryUsage();
this.samples.push(usage.heapUsed);
if (this.samples.length > this.maxSamples) {
this.samples.shift();
}
if (this.samples.length < this.maxSamples) {
return false;
}
const first = this.samples[0];
const last = this.samples[this.samples.length - 1];
const growth = last / first;
return growth > this.threshold;
}
startMonitoring(interval: number = 10000): void {
setInterval(() => {
if (this.checkForLeak()) {
console.warn("⚠️ Potential memory leak detected!");
console.warn(
"Memory samples:",
this.samples.map((s) => `${(s / 1024 / 1024).toFixed(2)} MB`),
);
}
}, interval);
}
}
// Usage
const detector = new LeakDetector();
detector.startMonitoring();Memory Monitoring in Production
Memory Monitoring in Production
class MemoryMonitor {
private alerts: Array<(usage: NodeJS.MemoryUsage) => void> = [];
startMonitoring(
options: {
interval?: number;
heapThreshold?: number;
rssThreshold?: number;
} = {},
): void {
const {
interval = 60000,
heapThreshold = 0.9,
rssThreshold = 0.95,
} = options;
setInterval(() => {
const usage = process.memoryUsage();
const heapUsedPercent = usage.heapUsed / usage.heapTotal;
if (heapUsedPercent > heapThreshold) {
console.warn(
`⚠️ High heap usage: ${(heapUsedPercent * 100).toFixed(2)}%`,
);
this.alerts.forEach((fn) => fn(usage));
// Force GC if available
if (global.gc) {
console.log("Forcing garbage collection...");
global.gc();
}
}
}, interval);
}
onAlert(callback: (usage: NodeJS.MemoryUsage) => void): void {
this.alerts.push(callback);
}
}
// Usage
const monitor = new MemoryMonitor();
monitor.onAlert((usage) => {
// Send alert to monitoring service
console.error("Memory alert triggered:", usage);
});
monitor.startMonitoring({
interval: 30000,
heapThreshold: 0.85,
});Node.js Heap Snapshots
Node.js Heap Snapshots
import v8 from "v8";
import fs from "fs";
class MemoryProfiler {
takeSnapshot(filename: string): void {
const snapshot = v8.writeHeapSnapshot(filename);
console.log(`Heap snapshot saved to ${snapshot}`);
}
getMemoryUsage(): NodeJS.MemoryUsage {
return process.memoryUsage();
}
formatMemory(bytes: number): string {
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
}
printMemoryUsage(): void {
const usage = this.getMemoryUsage();
console.log("Memory Usage:");
console.log(` RSS: ${this.formatMemory(usage.rss)}`);
console.log(` Heap Total: ${this.formatMemory(usage.heapTotal)}`);
console.log(` Heap Used: ${this.formatMemory(usage.heapUsed)}`);
console.log(` External: ${this.formatMemory(usage.external)}`);
}
monitorMemory(interval: number = 5000): void {
setInterval(() => {
this.printMemoryUsage();
}, interval);
}
}
// Usage
const profiler = new MemoryProfiler();
// Take initial snapshot
profiler.takeSnapshot("./heap-before.heapsnapshot");
// Run application
await runApp();
// Take final snapshot
profiler.takeSnapshot("./heap-after.heapsnapshot");
// Compare in Chrome DevTools to find leaksPython Memory Profiling
Python Memory Profiling
import tracemalloc
from typing import List, Tuple
class MemoryProfiler:
def __init__(self):
self.snapshots: List = []
def start(self):
"""Start tracking memory allocations."""
tracemalloc.start()
def take_snapshot(self):
"""Take a memory snapshot."""
snapshot = tracemalloc.take_snapshot()
self.snapshots.append(snapshot)
return snapshot
def compare_snapshots(
self,
snapshot1_idx: int,
snapshot2_idx: int,
top_n: int = 10
):
"""Compare two snapshots."""
snapshot1 = self.snapshots[snapshot1_idx]
snapshot2 = self.snapshots[snapshot2_idx]
stats = snapshot2.compare_to(snapshot1, 'lineno')
print(f"\nTop {top_n} memory differences:")
for stat in stats[:top_n]:
print(f"{stat.size_diff / 1024:.1f} KB: {stat.traceback}")
def get_top_allocations(self, snapshot_idx: int = -1, top_n: int = 10):
"""Get top memory allocations."""
snapshot = self.snapshots[snapshot_idx]
stats = snapshot.statistics('lineno')
print(f"\nTop {top_n} memory allocations:")
for stat in stats[:top_n]:
print(f"{stat.size / 1024:.1f} KB: {stat.traceback}")
def stop(self):
"""Stop tracking."""
tracemalloc.stop()
# Usage
profiler = MemoryProfiler()
profiler.start()
# Take initial snapshot
profiler.take_snapshot()
# Run code
data = [i for i in range(1000000)] # Allocate memory
# Take another snapshot
profiler.take_snapshot()
# Compare
profiler.compare_snapshots(0, 1)
profiler.stop()WeakMap/WeakRef for Cache
WeakMap/WeakRef for Cache
class WeakCache<K extends object, V> {
private cache = new WeakMap<K, V>();
set(key: K, value: V): void {
this.cache.set(key, value);
}
get(key: K): V | undefined {
return this.cache.get(key);
}
has(key: K): boolean {
return this.cache.has(key);
}
delete(key: K): void {
this.cache.delete(key);
}
}
// Objects can be garbage collected even if in cache
const cache = new WeakCache<object, string>();
let obj = { id: 1 };
cache.set(obj, "data");
// When obj is no longer referenced, it can be GC'd
obj = null as any;#!/bin/bash
# validate-config.sh - Validate infrastructure configuration
# Usage: ./validate-config.sh <config_file>
set -euo pipefail
CONFIG_FILE="${{1:?Usage: $0 <config_file>}}"
echo "Validating: $CONFIG_FILE"
# TODO: Add configuration validation logic
# - Check required fields
# - Validate syntax (YAML/JSON/HCL)
# - Verify referenced resources exist
# - Check for security best practices
echo "Validation complete."
# Infrastructure Configuration Starter
# TODO: Customize for your infrastructure setup
#
# Usage: Copy this file and modify for your environment
# --- Environment Configuration ---
environment: production
region: us-east-1
# --- Resource Definitions ---
# TODO: Add resource definitions specific to this skill's domain
# --- Security Settings ---
# TODO: Add security configuration
# --- Monitoring ---
# TODO: Add monitoring/alerting configuration
Related skills
How it compares
Use memory-leak-detection over general debugging skills when symptoms are gradual heap growth rather than crashes or logic errors.
FAQ
When should developers use memory-leak-detection?
Developers should use memory-leak-detection when heap usage climbs in long-running clients or services, especially before a release cut or after user performance complaints about gradual slowdown.
What symptoms does memory-leak-detection address?
The memory-leak-detection skill targets rising heap usage, orphaned event listeners, and retained objects that cause progressive memory bloat in production or staging environments.