Files
skills/docs/devtools/tooling/profiler/src/memory/memory.md
2026-05-29 08:20:56 +01:00

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 read process.memoryUsage then performance.memory then return zeros.
  • start() / stop() are idempotent — double-start and double-stop are no-ops.
  • takeSnapshot(label?) records a snapshot; FIFO-evicts beyond maxSnapshots.
  • recordAllocation(label, bytes) aggregates allocations / totalBytes / peakBytes per label.
  • detectLeak() returns true when the last leakWindowSize snapshots are monotonically non-decreasing AND total growth meets leakThresholdBytes.
  • triggerGC() invokes globalThis.gc() when Node was started with --expose-gc.
  • events: EventBus<MemoryProfilerEventMap> — memory:start, memory:stop, memory:snapshot, memory:leak-detected.
  • Telemetry: profiler_memory_snapshots_total counter 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.