Heap Snapshot vs Allocation Timeline: When to Use Which
You have two DevTools memory recorders open in tabs and cannot decide which one actually answers your question — this guide, part of interpreting heap snapshots inside the wider Browser DevTools & Performance Profiling Workflows area, gives you a decision rule and a side-by-side matrix.
| Symptom | Root Cause | Immediate Action |
|---|---|---|
| Heap stays high after closing a view | Retained objects, wrong tool question | Take two heap snapshots, compare |
| Heap climbs continuously during scroll | High allocation rate, not retention | Record an allocation timeline |
| Snapshot comparison shows small delta | Leak is rate-based, not size-based | Switch to timeline, watch bar color |
| Timeline shows huge GC sawtooth | Short-lived churn, likely not a leak | Confirm bars turn gray after sweep |
| Can’t tell what’s holding a growing object | Timeline lacks retainer detail | Snapshot at the same point, inspect Retainers |
Root Cause
The confusion is not a UI problem — it is that a heap snapshot and an allocation timeline answer two structurally different questions, and picking the wrong one produces a technically correct but useless answer. A heap snapshot is a single serialised graph of the entire V8 object heap at one instant: every object, every edge between them, and the dominator tree DevTools derives from that graph. It tells you what is alive right now and, via retained size, what would be freed if a given object were collected. It has no concept of time — it cannot tell you whether an object was allocated a millisecond ago or an hour ago, only that it currently exists and is reachable. This is the tool covered in depth in interpreting heap snapshots for memory analysis, and its natural companion technique — comparing two snapshots to isolate what survived — is documented in how to take and compare heap snapshots in Chrome step by step.
An allocation timeline, by contrast, is a continuous recording of allocation events over a window of wall-clock time. DevTools instruments the allocator itself: every object creation is logged with a timestamp, a size, and (in the sampling variant) a stack trace, then rendered as a series of vertical bars along a horizontal time axis. Bar height maps to bytes allocated in that time bucket; bar color encodes survival — blue means the memory allocated in that bucket is still live, gray means it has since been collected. This is the mechanism explained in using allocation timelines to track object creation, and the practical leak-hunting pattern built on top of it is in reading allocation timelines to identify memory leaks.
The practical consequence: a snapshot answers “what is retained and why”, a timeline answers “when and how fast is allocation happening, and does it survive GC”. A snapshot comparison can miss a slow leak if the two capture points happen to straddle a moment where the leaking allocation hasn’t yet accumulated to a visible delta. A timeline can show you unmistakably that memory is climbing but gives you no retainer chain — you cannot expand a blue bar and see who is holding the reference. Neither tool substitutes for the other; each is instrumentation for a different variable, size versus rate.
Step-by-Step Fix
Follow this order rather than guessing which recorder to open first — it saves a re-run when the first tool answers the wrong question.
-
Classify the complaint in one sentence. Write down whether the issue is “memory stays high after I stop doing X” (retention) or “memory keeps climbing while I do X” (allocation rate). Expected output: a one-line classification that tells you which recorder to open next.
-
For retention questions, open DevTools → Memory → Heap Snapshot. Take a baseline snapshot, perform the suspect action 3-5 times, click the trash-can icon to force GC, then take a second snapshot. Switch to DevTools → Memory → Snapshot 2 → Comparison view and sort by Delta. Expected output: a ranked list of object constructors with positive delta counts — these are the candidates that survived.
-
For rate questions, open DevTools → Memory → Allocation instrumentation on timeline. Click Start, exercise the workload for 10-30 seconds, then click Stop. Expected output: a bar chart where bar height is bytes-per-bucket; hover any bar to see the allocation count and average size for that bucket.
-
Trigger a manual GC mid-recording and watch bar color. With the timeline still open, click the trash-can icon in the DevTools → Memory toolbar without stopping the recording. Expected output: bars from before the GC should turn from blue to gray if their memory was collectable; bars that stay blue across the sweep are your leak candidates.
-
Cross-validate a timeline finding with a snapshot. Once a timeline shows a persistent blue region, pause execution at that point (or take a snapshot immediately after stopping the recording) via DevTools → Memory → Heap Snapshot. Expected output: the same constructor that showed persistent blue bars now appears in the snapshot’s Summary view with a non-trivial retained size — select it and expand Retainers to find the holding reference.
-
Confirm the fix closes both signals. After patching the retainer (removing a listener, clearing a cache, nulling a reference), repeat step 2’s two-snapshot comparison and step 3’s timeline recording. Expected output: snapshot delta approaches zero and the timeline shows all bars turning gray within one or two GC sweeps.
Command & Code Reference
Use this Puppeteer script to automate the “which tool do I need” decision by capturing both a heap snapshot delta and a coarse allocation-rate proxy in one CI run.
const puppeteer = require('puppeteer');
// Launch with the CDP endpoint exposed for heap profiling calls
const browser = await puppeteer.launch({
headless: 'new',
args: ['--js-flags=--expose-gc'],
});
const page = await browser.newPage();
const client = await page.target().createCDPSession();
// Enable the heap profiler domain before taking any snapshot
await client.send('HeapProfiler.enable');
// Force a GC baseline, mirroring the trash-can icon in DevTools
await client.send('HeapProfiler.collectGarbage');
const before = await client.send('Runtime.evaluate', {
expression: 'performance.memory.usedJSHeapSize',
returnByValue: true,
});
// Run the workload N times to amplify any retention signal
for (let i = 0; i < 5; i++) {
await page.click('#open-suspect-view');
await page.click('#close-suspect-view');
}
// Force GC again, then read the post-workload heap size
await client.send('HeapProfiler.collectGarbage');
const after = await client.send('Runtime.evaluate', {
expression: 'performance.memory.usedJSHeapSize',
returnByValue: true,
});
// Delta in bytes; a healthy component returns near zero
const deltaBytes =
after.result.value - before.result.value;
const deltaMB = (deltaBytes / 1024 / 1024).toFixed(2);
console.log(`Retained delta after 5 cycles: ${deltaMB} MB`);
Use this snippet to sample the allocation rate directly in Node.js when you need a timeline-style signal outside the browser, such as in an SSR worker.
const v8 = require('v8');
// Read heap statistics twice, 5 seconds apart, without forcing GC
const t0 = v8.getHeapStatistics();
await new Promise((r) => setTimeout(r, 5000));
const t1 = v8.getHeapStatistics();
// used_heap_size delta over the interval approximates allocation
// rate when no GC has run; a true rate needs GC-event correlation
const bytesPerSec =
(t1.used_heap_size - t0.used_heap_size) / 5;
const kbPerSec = (bytesPerSec / 1024).toFixed(1);
console.log(`Approx allocation rate: ${kbPerSec} KB/s`);
Verification & Regression Prevention
Set explicit thresholds so the choice of tool stops being subjective and becomes a pass/fail gate:
- Retention target: two-snapshot delta after 5 repeated cycles of the same UI action should be under 500 KB. Anything above that on a component that is supposed to fully unmount is a leak, not noise.
- Allocation rate target: for steady-state idle views (no user interaction), the allocation timeline should show fewer than 50 KB of persistent blue bars per 10-second window after two GC sweeps.
- CI hook: wire the Puppeteer script above into a post-merge job and fail the build if
deltaMBexceeds a checked-in baseline by more than 10%, usingprocess.exitCode = 1when the threshold is breached. - Monitoring in production: if you expose
performance.memorymetrics via Real User Monitoring, alert whenusedJSHeapSizegrows for more than 3 consecutive samples without a corresponding drop after a route change — that pattern maps directly to the “blue bars never turn gray” signature from the timeline view.
Frequently Asked Questions
Can I use both a heap snapshot and an allocation timeline in the same session?
Yes, and it is the recommended workflow. Record an allocation timeline first to find when and how fast allocation is happening, then take a heap snapshot at the point the timeline shows retained blue bars to get the exact retainer chain.
Why does the allocation timeline show blue bars that never turn gray?
Blue bars represent live allocations at the time of the recording; DevTools grays out a bar only after a garbage collection sweeps that memory. A blue bar that stays blue across multiple GC sweeps means those objects are still reachable from a GC root — that is your leak signature.
Does allocation timeline recording slow down the page more than a heap snapshot?
Yes, materially. Allocation instrumentation on timeline attaches a stack trace to every allocation for the duration of the recording, which can slow execution by 2-5x. A heap snapshot pauses the page briefly to serialise the graph but does not instrument ongoing execution, so it has negligible steady-state overhead.
Related
- Interpreting Heap Snapshots for Memory Analysis — the parent guide covering dominator trees and retained size in depth.
- Using Allocation Timelines to Track Object Creation — how the instrumentation-on-timeline recorder works internally.
- How to Take and Compare Heap Snapshots in Chrome Step by Step — the exact three-snapshot capture workflow referenced above.
- Reading Allocation Timelines to Identify Memory Leaks — the bar-color leak-hunting pattern in full detail.
- Browser DevTools & Performance Profiling Workflows — the main section for all DevTools-based diagnostic techniques.