Why Chrome Task Manager and DevTools Report Different Memory

Chrome’s Task Manager says your tab uses 900 MB while a heap snapshot in DevTools totals 120 MB, and you need to know which number is real — this guide sits under Mastering the Chrome DevTools Memory Tab within Browser DevTools & Performance Profiling Workflows, and explains what each tool actually counts.

Symptom Root Cause Immediate Action Measurable Impact
Task Manager “Memory footprint” is 5–10× the heap snapshot total Footprint counts the whole renderer process: DOM, images, GPU buffers, code, fonts Enable the JavaScript memory column and compare to it instead Removes 70–90% of the apparent discrepancy
JavaScript memory column shows “400 MB (150 MB live)” First figure is reserved heap, parenthesised figure is used heap Track the live figure over time, not the reserved one Stops chasing space V8 has reserved but not filled
Heap snapshot is smaller than the Performance panel’s JS heap line Taking a snapshot forces a full GC and records only reachable objects Compare snapshots with snapshots, and timelines with timelines Consistent baselines between runs
Two tabs of the same site show identical, huge memory Both tabs share one renderer process under site isolation Check the process ID column; profile one tab at a time Correct attribution of memory to a single page
Memory drops sharply when you open DevTools Opening DevTools and snapshotting triggers GC Read Task Manager before attaching DevTools Avoids an observer effect hiding real growth

Root Cause: Four Different Questions, Four Different Numbers

Chrome’s memory tools each answer a different question, and the numbers only look contradictory because they are measuring different boundaries. The widest boundary is the Memory footprint column in Chrome Task Manager (⋮ → More tools → Task manager, or Shift+Esc on Windows and Linux). It reports the private memory of the renderer process: the V8 JavaScript heap, but also Blink’s DOM and layout objects, decoded images, style data, compiled code, font caches, network buffers, and memory allocated by the GPU path on behalf of the page. A single high-resolution image gallery can add hundreds of megabytes of decoded bitmaps that never appear in a JavaScript heap snapshot at all.

The next boundary is the JavaScript memory column, which is hidden by default — right-click the Task Manager column header to enable it. It shows two figures such as 412,000K (156,000K live). The first is how much memory V8 has reserved for this renderer’s JavaScript heaps; the second, in parentheses, is how much of that reservation is occupied by live objects. V8 grows its reservation in chunks and returns it lazily, so the first figure lags behind real usage in both directions. The parenthesised figure is the one that moves with your code.

The narrowest boundary is the heap snapshot. Before serialising the heap, DevTools asks V8 to run a full garbage collection, so everything unreachable is gone. What remains is only the reachable object graph for the selected JavaScript context. That is why a snapshot total is almost always smaller than the live figure you saw a second earlier: garbage that was still sitting in the heap has just been collected. The Performance panel’s memory track sits in between — it samples the used JS heap during recording without forcing collection, so it shows the sawtooth of allocation and garbage collection rather than the post-GC floor.

Process sharing adds one more twist. With site isolation, Chrome usually groups tabs from the same site into one renderer process, so two tabs of your app can report the same footprint because they are the same process. The Task Manager’s Process ID column makes this visible, and it is the first thing to check before assuming one page is responsible for the whole figure.

Nested measurement boundaries in Chrome The outermost box is the renderer process memory footprint reported by Task Manager, including DOM, images, GPU and code. Inside it is the reserved JavaScript heap, the first figure in the JavaScript memory column. Inside that is the live JavaScript heap, the parenthesised figure. The innermost box is the reachable object graph a heap snapshot records after forcing garbage collection. Memory footprint (Task Manager) — e.g. 900 MB DOM + layout, decoded images, GPU buffers, compiled code, fonts, network buffers JavaScript memory, reserved — 412 MB space V8 has claimed; shrinks lazily Live JS heap — 156 MB parenthesised figure; includes garbage Heap snapshot — 120 MB reachable only, after forced GC

Step-by-Step Fix

  1. Read Task Manager before opening DevTools. Open ⋮ → More tools → Task manager, right-click the header and enable JavaScript memory and Process ID. Note the footprint and the live JS figure for your tab. Verification: you have two numbers, for example Memory footprint 910 MB and JavaScript memory 412,000K (156,000K live).
  2. Check whether the process is shared. Look for other tabs or iframes with the same Process ID. If several rows share it, close the others or open your page in a fresh profile. Verification: your tab is the only row with that process ID.
  3. Split the footprint into JS and non-JS. Subtract the live JS figure from the footprint. If the remainder is large (hundreds of MB), the growth lives in the DOM, images or GPU memory rather than in JavaScript objects. Verification: you can say which side of the split is growing when you repeat the user flow.
  4. Capture the post-GC floor. Open DevTools → Memory → Heap snapshot and take one snapshot. Its total in the sidebar is the reachable JavaScript floor. Verification: the snapshot total is at or below the live JS figure from step 1; a large gap means a lot of collectable garbage was pending.
  5. Record the trend, not the level. Open DevTools → Performance, tick Memory, and record the flow three times. Watch whether the JS heap line’s troughs rise between repetitions. Verification: flat troughs mean no JS retention even if the footprint is high; rising troughs mean a leak worth snapshot-diffing.
  6. Pick one number per question and log it. Use the live JS figure (or performance.measureUserAgentSpecificMemory() in the field) for JavaScript leaks, and the footprint for “will this tab be killed on a low-memory device”. Verification: your bug report states which metric regressed and by how many MB.
Which number answers which question Three question boxes on the left each point to the tool that answers them. Is my JavaScript leaking points to heap snapshot comparison and the live JS heap trend. Will the tab be killed on a phone points to Task Manager memory footprint. Is garbage collection hurting frame rate points to the Performance panel memory track. Is my JavaScript leaking? Snapshot diff + live JS trend DevTools → Memory, Task Manager (live) Will the tab be killed on a phone? Memory footprint Task Manager, whole renderer process Is GC hurting frame rate? Performance panel memory track sawtooth depth and GC event count

Command and Code Reference

Use case: log the same numbers from inside the page so you can correlate them with user actions. performance.memory is Chromium-only and coarse, but it mirrors the reserved and used JS heap figures; measureUserAgentSpecificMemory() is the modern, more complete replacement when the page is cross-origin isolated.

// memory-probe.js — paste into the DevTools Console or ship behind a debug flag
async function memoryProbe(label) {
  const mb = (n) => (n / 1048576).toFixed(1) + ' MB';
  const out = { label };

  // Legacy, Chromium-only: used vs reserved JS heap for this context
  if (performance.memory) {
    out.jsUsed = mb(performance.memory.usedJSHeapSize);      // ~ the "live" figure
    out.jsReserved = mb(performance.memory.totalJSHeapSize); // ~ the reserved figure
  }

  // Modern API: includes DOM and iframes, requires crossOriginIsolated
  if (self.crossOriginIsolated && performance.measureUserAgentSpecificMemory) {
    const result = await performance.measureUserAgentSpecificMemory();
    out.uaTotal = mb(result.bytes); // may take seconds: it waits for a GC
  }
  console.table(out);
}

memoryProbe('after opening report');

Use case: capture the renderer footprint from outside the page in an automated run. The Chrome DevTools Protocol exposes the same process-level counters the Task Manager reads.

// footprint.mjs — node footprint.mjs  (needs puppeteer)
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173/', { waitUntil: 'networkidle0' });

// JSHeapUsedSize ≈ live JS; Nodes and Documents expose DOM growth
const m = await page.metrics();
console.log({
  jsUsedMB: (m.JSHeapUsedSize / 1048576).toFixed(1),
  jsTotalMB: (m.JSHeapTotalSize / 1048576).toFixed(1),
  domNodes: m.Nodes,          // non-JS memory driver #1
  documents: m.Documents,     // leaked iframes show up here
});
await browser.close();
Footprint grows, JavaScript does not Over five repetitions of a user flow the Task Manager footprint line climbs from 600 to 900 megabytes, while the live JavaScript heap troughs stay flat near 120 megabytes. The gap indicates growth outside JavaScript, such as decoded images or DOM. 900 MB 500 MB 0 flow repetitions 1 → 5 Memory footprint (Task Manager) Live JS heap: flat troughs gap = images, DOM or GPU growth

Verification and Regression Prevention

You have resolved the confusion when every memory figure in your bug report is labelled with its source and boundary: “live JS heap (Task Manager) rose from 150 MB to 310 MB over five flows” is actionable; “Chrome uses 900 MB” is not. For JavaScript retention, the target is flat heap troughs across repetitions and a snapshot comparison that shows no constructor growing by more than a few hundred KB per flow. For whole-process growth, the target is a footprint that returns to within 10% of its starting level after the flow is closed.

Guard against regressions by tracking both boundaries in automation. Run the Puppeteer metrics script after each repetition of a scripted flow and fail CI when JSHeapUsedSize grows more than 10 MB across five repetitions, or when Nodes keeps rising — DOM growth is the most common cause of a footprint that JavaScript numbers do not explain, and it links directly to detached DOM node retention. For the process-level view in real users’ browsers, see how the Memory tab compares with the Performance memory lane.

Frequently Asked Questions

Which number should I report in a memory bug?

Report the live JavaScript heap trend if you suspect a JavaScript leak, and the Task Manager memory footprint if the complaint is that the tab crashes or slows the device. Always include how the number changed across repetitions rather than a single reading, because both figures fluctuate with garbage collection timing.

Why does memory go down when I open DevTools?

Opening DevTools, and especially taking a heap snapshot, forces garbage collection. Garbage that was waiting to be collected disappears, so the numbers drop. Read the Task Manager figures before attaching DevTools if you want the undisturbed baseline.

Does the heap snapshot include DOM nodes?

It includes the JavaScript wrappers for DOM nodes and entries for native DOM objects that JavaScript can reach, which is why detached nodes appear in snapshots. It does not include decoded image pixels, GPU textures, or most rendering structures, which only show up in the Task Manager footprint.