3.1 KiB
memory
(no submodules)
Purpose
MemoryProfiler captures periodic heap snapshots, records labelled allocation totals, tracks a peak-heap watermark, and runs a simple trend-based leak detector. Heap readings come from an injectable HeapCapture so test runs are deterministic and non-Node environments can plug their own probe. Timer source is TimersLike, matching Sampler.
Features
- Zod-validated config (
sampleIntervalMs > 0,maxSnapshots > 0,leakThresholdBytes ≥ 0,leakWindowSize ≥ 2). - Injectable
Clock,TimersLike,HeapCapture. Defaults readprocess.memoryUsagethenperformance.memorythen return zeros. start()/stop()are idempotent — double-start and double-stop are no-ops.takeSnapshot(label?)records a snapshot; FIFO-evicts beyondmaxSnapshots.recordAllocation(label, bytes)aggregatesallocations/totalBytes/peakBytesper label.detectLeak()returns true when the lastleakWindowSizesnapshots are monotonically non-decreasing AND total growth meetsleakThresholdBytes.triggerGC()invokesglobalThis.gc()when Node was started with--expose-gc.events: EventBus<MemoryProfilerEventMap>—memory:start,memory:stop,memory:snapshot,memory:leak-detected.- Telemetry:
profiler_memory_snapshots_totalcounter on every snapshot.
Usage: Basic
import { MemoryProfiler } from "@suaveplan/profiler";
const mp = new MemoryProfiler({ sampleIntervalMs: 1000 });
mp.start();
// ...
mp.stop();
if (mp.detectLeak()) console.warn("leak suspected");
Usage: Advanced
import { MemoryProfiler } from "@suaveplan/profiler";
const fakeHeap: Array<{ heapUsed: number; heapTotal: number }> = [
{ heapUsed: 1_000_000, heapTotal: 10_000_000 },
{ heapUsed: 2_000_000, heapTotal: 10_000_000 },
{ heapUsed: 3_000_000, heapTotal: 10_000_000 },
];
let i = 0;
const mp = new MemoryProfiler({
leakWindowSize: 3,
leakThresholdBytes: 1_500_000,
heapCapture: () => fakeHeap[i++] ?? fakeHeap[fakeHeap.length - 1]!,
});
mp.events.on("memory:leak-detected", ({ snapshots }) => {
log("leak window", snapshots);
});
for (let n = 0; n < 3; n++) mp.takeSnapshot();
mp.detectLeak(); // true → emits memory:leak-detected
API Reference
class MemoryProfiler—new MemoryProfiler(options?: MemoryProfilerOptions).interface MemoryProfilerOptions extends MemoryProfilerConfig { clock?, timers?, heapCapture? }.- Methods:
start,stop,takeSnapshot,recordAllocation,getAllocations,getAllocation,getSnapshots,getPeakHeapUsed,isRunning,detectLeak,triggerGC,clear.
Implementation Notes
The leak detector walks the last leakWindowSize snapshots and counts strictly-increasing transitions; a leak is declared only when EVERY transition is non-decreasing AND end-minus-start exceeds the threshold. That eliminates the false-positive case where one large jump in an otherwise flat window would otherwise read as a leak. triggerGC is a best-effort hook — Node only exposes globalThis.gc under --expose-gc, so the false branch matters in production.