completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Finding memory leaks

Use Chrome DevTools heap snapshots, comparison views, retainers, and detached DOM clues to find and verify JavaScript memory leaks.

By the end, you can
  • 01
    Pick the right memory toolChoose a heap snapshot, the allocation timeline, allocation sampling, or Performance monitor, based on the question you have.
  • 02
    Read retainer pathsUse shallow size, retained size, distance, and the Retainers pane to explain why an object is still in memory.
  • 03
    Check a leak fixRepeat one action, compare snapshots, fix the root that keeps the memory, and show that the comparison stops growing.

Prove what keeps memory in use

JavaScript keeps the objects your code makes in an area of memory called the heap. When nothing can reach an object any more, the engine frees its memory. This cleanup is called garbage collection, or GC for short. The memory management lesson explains it step by step.

A memory leak happens when your code keeps a link to an object it no longer needs. Then the engine can't free that object. If this happens again and again, the heap keeps growing.

This lesson shows how to find a leak with Chrome DevTools, the tools built into the Chrome browser. The WeakRef & FinalizationRegistry lesson showed why weak references are rarely the fix. Here you use DevTools to find which object still holds the memory.

Plain definition

Finding a memory leak means proving that an object is still in memory after the feature that used it is gone. Then you follow the links back to the place in your code that holds it, and you clean that place up.

You need this in real apps, such as:

  • a single-page app that switches pages without reloading;
  • a dashboard that stays open all day;
  • a long list that loads more rows as you scroll;
  • a pop-up window, called a modal, that opens and closes hundreds of times;
  • a developer tool that keeps large amounts of loaded data.

The steps are always the same:

  1. Measure the memory.
  2. Pick one action to test.
  3. Take heap snapshots and compare them.
  4. Follow the links to the object that holds the memory.
  5. Fix the code that keeps that link.
  6. Compare again to check the fix.

The Memory panel: which tool answers which question?

DEVTOOLS MAP

Open Chrome DevTools and choose the Memory panel. It has several tools. Some of them talk about allocation: setting aside memory for a new object. Each tool answers a different question:

  • Heap snapshot: what is in memory right now?
  • Allocation instrumentation on timeline: which objects were made during this action, and are they still there at the end?
  • Allocation sampling: which code makes the most objects over a long run?
  • Detached elements: which removed page elements does JavaScript still hold?

The table lists these tools and two more. Read each row as: this tool records this, so use it when you need this.

Memory tools and when to use them
ToolWhat does it record?When should you use it?
Heap snapshotA graph of the JavaScript objects and page elements in memory at one moment.Find what keeps memory, and compare snapshots from before and after an action.
Allocation instrumentation on timelineEvery new object over time. Objects still in memory at the end are marked.Find which repeated action makes objects that never go away.
Allocation samplingOnly some new objects, grouped by the function calls that made them. It slows the page down less.Study long sessions or code that makes many objects, when the exact objects matter less.
Performance monitorLive graphs, such as the JS heap size, while you use the page.Notice a trend, then choose a smaller action to look into.
Performance panel Memory checkboxA heap graph lined up with scripts, drawing, and events in a performance recording.Link memory growth to long tasks, page changes, or clicks.

A heap snapshot can be shown in four views:

  • Summary groups objects by their constructor, the function that made them, such as Array or Map.
  • Comparison shows how the count of each kind of object changed between two snapshots.
  • Containment starts from the starting points, such as Window, and lets you open what they hold.
  • Statistics shows a pie chart of what uses the memory.

The toolbar also has a button that runs garbage collection right away. It has a filter by constructor name, too, and filters such as "objects made between two snapshots" or "objects kept by detached nodes".

A rising memory graph is a clue, not proof

The measuring performance lesson covers performance recordings. To spot a leak, open Performance monitor, or turn on the Memory checkbox in the Performance panel. Watch the JS heap size. If it keeps rising even after garbage collection, switch to the Memory panel. There you can see which objects stay and what keeps them.

A heap snapshot is a graph

MODEL

A heap snapshot is not a picture of all the computer's memory. It is a graph: a set of JavaScript objects and page elements, joined by links. Each link is a reference: one object points to another. Here are the words you need:

  • A root is a starting point that is always kept, such as window. DevTools calls these GC roots.
  • Roots point to objects, and objects point to more objects.
  • A retainer is an object that keeps another object in memory by pointing to it. The Retainers pane lists every object that points to the object you selected.
  • Retained size is the memory that would be freed if one object were gone. It counts the object itself, plus every object you can reach only through it.
  • If every path from a root to object B goes through object A, DevTools says A dominates B. Then freeing A would free B too.
Real-life analogyFinding who still has a library book

A book should have come back to the library, but it didn't. You don't guess who has it from how heavy it is. You read the checkout records until you find the account that still has it.

Heap snapshots work the same way. You follow the links. You don't blame the biggest object first.

In real life: The library's checkout desk and account records
In JavaScript: GC roots, such as window, the browser's timer list, and variables at the top of a module
In real life: A reader's account lists a borrowed book
In JavaScript: A reference from one object to another
In real life: A family account holds all the children's cards
In JavaScript: An object that dominates others: its retained size counts everything only it reaches
In real life: The checkout record shows who can return the book
In JavaScript: The Retainers pane leads back to the root you can fix

The table explains the words you read in a snapshot. Read each row as: this word means this, and you use it like this.

Words you read in a snapshot
WordWhat does it mean?How do you use it?
Shallow sizeThe memory one object holds itself, such as an array's own storage or one page element.Use it to spot single big objects. Don't stop there, though.
Retained sizeThe memory that would be freed if this object were gone. It counts the object plus everything you can reach only through it.Use it to find small objects that keep many other objects in memory.
DistanceHow many links the shortest path takes from a root, such as window, the browser's own roots, or its timer list.A small distance often means an owner that lives a long time is close by.
RetainersThe objects that point to the selected object.Follow them back to a root: a Map, a listener, a timer, a closure, or the console.
The panel:1 object has its own size, 120. It points to button:1, size 80, and text:1, size 32. Nothing else reaches those two, so the retained size of panel:1 is 120 + 80 + 32 = 232.panel:1size 120button:1size 80text:1size 32retained size of panel:1120 + 80 + 32 = 232
The panel's own size is 120. Only the panel reaches the button and the text, so its retained size counts them too: 232.

The next step-through uses a small, made-up heap that always gives the same result. The lesson provides four helper functions, so Edit & run works on the code exactly as shown:

  • createLeakSnapshot(n) builds a made-up heap after n leaky actions.
  • computeReachable lists every object the roots can reach.
  • computeRetainedSizes works out the retained size of each object.
  • findRetainerPath finds the shortest path from a root to one object.
Find what keeps a leaked button
Step 0 of 7Ready
Your turn: follow the blue line

You used the made-up heap like DevTools: start at the roots, find the path that keeps a leaked object, and compare shallow size with retained size.

Running in
  1. script
Next: line 1
const reachable = computeReachable(snapshot);const retained = computeRetainedSizes(snapshot);const path = findRetainerPath(snapshot, "button:1"); console.log(reachable.length);console.log(path.join(" -> "));console.log(retained["panel:1"]);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Take and compare heap snapshots

3 SNAPSHOTS

A reliable way to find growth is called the three-snapshot technique. Before each snapshot, press the button that runs garbage collection, so only objects that are really kept remain.

  1. Load the page. Do the action once as a warm-up, so code that loads late and caches can settle. Take Snapshot 1.
  2. Do the action you suspect once more. Take Snapshot 2.
  3. Repeat the same action several more times. Take Snapshot 3.
Three boxes in a row. Snapshot 1 is taken after a warm-up. Snapshot 2 is taken after 1 action. Snapshot 3 is taken after 5 actions. Comparing Snapshot 2 with Snapshot 3 shows 4 more detached panels.warm-upSnapshot 1check warm-up1 actionSnapshot 2starting point5 actionsSnapshot 3after repeatscompare 2 and 3: +4 panels
Snapshot 1 only checks that the warm-up is done. You compare Snapshot 2 (1 action) with Snapshot 3 (5 actions), and the 4 extra panels are the growth.

Open Snapshot 3 in the Comparison view, against Snapshot 2. Each snapshot has a job:

  • Snapshot 1 checks that the warm-up is done.
  • Snapshot 2 is the starting point, often called the baseline, that you measure from.
  • Snapshot 3 shows what grew while you repeated the action.

The change in a count between two snapshots is called the delta. A constructor row with a growing delta is a suspect, not proof. For proof, pick one object in that row. Then follow its retainers back to a root in your code.

Compare Snapshot 2 with Snapshot 3
Step 0 of 8Ready
Your turn: follow the blue line

This is the Snapshot 2 to Snapshot 3 comparison from the three-snapshot technique. Next, follow the retainers of the rows that grew.

Running in
  1. script
Next: line 1
const after = createLeakSnapshot(5);const diff = diffConstructorCounts(before, after);const detached = diff.find((row) => row.name === "DetachedHTMLDivElement");const functions = diff.find((row) => row.name === "Function");console.log(detached.delta);console.log(functions.delta);console.log(diff.filter((row) => row.delta > 0).map((row) => row.name + ":" + row.delta).join(", "));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The step-through only shows the last part: Snapshot 2 compared with Snapshot 3. Its before heap has one action. Its after heap has five. So the difference is four extra actions, not five.

In Chrome's Summary view, the constructor filter can show Objects allocated between Snapshots 2 and 3. In the Comparison view, sort by the positive deltas, open a constructor, and pick one object. Then read the Retainers pane. The playground below shows these deltas without making the browser use real memory.

Compare two snapshots
Compare the two heapsPop out in the code editor (opens in a new tab)JavaScript
const before = createLeakSnapshot(1);const after = createLeakSnapshot(5);const diff = diffConstructorCounts(before, after);const detached = diff.find((row) => row.name === "DetachedHTMLDivElement");const functions = diff.find((row) => row.name === "Function");console.log(detached.delta);console.log(functions.delta);console.log(diff.filter((row) => row.delta > 0).map((row) => row.name + ":" + row.delta).join(", "));
Change in each count5 actions
Array+4

Snapshot 2: 1, Snapshot 3: 5

Context+4

Snapshot 2: 1, Snapshot 3: 5

DetachedHTMLDivElement+4

Snapshot 2: 1, Snapshot 3: 5

Function+8

Snapshot 2: 2, Snapshot 3: 10

HTMLButtonElement+4

Snapshot 2: 1, Snapshot 3: 5

Text+4

Snapshot 2: 1, Snapshot 3: 5

Try it yourself

Snapshot 2 has one action and Snapshot 3 has 5. So 4 extra detached panels and 8 extra callbacks stayed.

This uses the made-up heap, so it always gives the same counts. It shows how to read the Comparison view; it doesn't look at this page's real memory.

Follow the retainers back to a root

ROOT CAUSE

A retainer chain answers one question: who still points at this object? Start at an object that should be gone. Then follow the Retainers pane, one link at a time, until you reach a root your code owns.

The box moduleCache, a root, points to the card panel:1, which points to the card button:1. The path is moduleCache -> panel:1 -> button:1.who still points at button:1?moduleCachethe rootpanel:1button:1you picked thismoduleCache -> panel:1 -> button:1
You pick button:1 and ask who points at it. The answer leads back to moduleCache, the root your code can clean up.

Chains often end at one of these:

  • a Map stored in a variable at the top of a module;
  • a listener on window;
  • a timer callback;
  • a closure;
  • a cache inside a framework;
  • an object you looked at in the DevTools console.

Here are four common ones, and how to fix each:

  • Module cache: a Map at the top of a module stores page elements or page data. It never deletes old entries. Fix it with a size limit, or delete old entries yourself. If you only attach extra data to objects, use a WeakMap or WeakSet.
  • Event listener: window or document keeps a callback, and the callback uses data from a page that is closed. Fix it with removeEventListener or an AbortController. If the listener should run only once, pass { once: true }. The events lesson explains how listeners work.
  • Timer: the browser's timer list keeps a setInterval callback alive. Fix it with clearInterval or clearTimeout when the feature closes. The timers lesson explains the timer list.
  • Closure: a closure is a function plus the variables it remembers from where it was made. A callback that lives a long time may remember more than it needs. Keep only an ID or a count when that is enough. The closures lesson explains them.
Retained size counts only what one object holds alone

If two separate roots can reach the same object, neither root alone dominates it. So retained size can surprise you:

  • A small object can have a big retained size when it is the only way to reach many objects.
  • A big object that two owners share may stay in memory when you delete just one owner.

Detached DOM nodes

LIVE DEMO

The DOM is the tree of elements that makes up the page. Each element in it is a node. A detached DOM node is a node that was removed from the tree, but that JavaScript still points at. So the engine can't free it.

In a snapshot, you can find detached nodes with the Detached elements tool, with the detached-node filters, or by a constructor name such as DetachedHTMLDivElement. The real clue is the retainer path: an array, a listener, a closure, a cache, or the console still points at the node.

Removed from the page, but still keptPop out in the code editor (opens in a new tab)JavaScript
const retainedNodes = [];const leakedListeners = []; function mountAndLeakPanel() {  const panel = document.createElement("section");  panel.className = "preview-panel";  panel.textContent = "Preview panel";  const onResize = () => panel.textContent.length;  window.addEventListener("resize", onResize);  document.body.append(panel);  panel.remove();  retainedNodes.push(panel);  leakedListeners.push(onResize);} mountAndLeakPanel();console.log(document.querySelector(".preview-panel") === null);console.log(retainedNodes.length);console.log(retainedNodes[0].isConnected);for (const listener of leakedListeners) window.removeEventListener("resize", listener);retainedNodes.length = 0;

The code makes a panel, adds it to the page, and removes it. Then it prints three lines:

  • true: the search for .preview-panel finds nothing, so the panel is not in the page.
  • 1: the retainedNodes array still holds the panel.
  • false: the panel's isConnected property is false, which means it is not attached to the page.

The last two lines clean up this demo. They remove the listeners and empty the array.

Left: the page window has no panel, so document.querySelector finds nothing. Right: the removed section panel still exists. The retainedNodes array and the onResize listener both point to it, so it stays in memory.pagepanel removedquerySelector: nullthe removed panel<section>retainedNodesonResizearraylistener
panel.remove() takes the panel out of the page. But the retainedNodes array and the onResize listener still point at it, so it stays in memory.

The fix uses an AbortController. It gives you a signal. Pass the signal when you add a listener. Later, call controller.abort(), and the browser removes that listener.

The fix: abort the listener and keep no nodePop out in the code editor (opens in a new tab)JavaScript
const retainedNodes = []; function mountAndCleanPanel() {  const controller = new AbortController();  const panel = document.createElement("section");  panel.className = "preview-panel";  panel.textContent = "Preview panel";  const onResize = () => panel.textContent.length;  window.addEventListener("resize", onResize, { signal: controller.signal });  document.body.append(panel);  panel.remove();  controller.abort();} mountAndCleanPanel();console.log(document.querySelector(".preview-panel") === null);console.log(retainedNodes.length);console.log("listener aborted");

This version prints true, 0, and listener aborted. The panel is gone from the page, the array is empty, and no listener points at the panel any more.

Leaky panel versus fixed panel
A leaky panel and a fixed panelPop out in the code editor (opens in a new tab)JavaScript
const retained = []; function leakyMountUnmount(host) {  const panel = document.createElement("section");  panel.textContent = "Preview";  const onResize = () => panel.textContent.length;  window.addEventListener("resize", onResize);  host.append(panel);  panel.remove();  retained.push({ panel, onResize });} function fixedMountUnmount(host) {  const controller = new AbortController();  const panel = document.createElement("section");  panel.textContent = "Preview";  window.addEventListener("resize", () => panel.textContent.length, { signal: controller.signal });  host.append(panel);  panel.remove();  controller.abort();}
What the demo keepssix at most
Detached panels the demo array keeps0
Leaky listeners still on0
Fixed panels added and removed0

Update 0. The demo never keeps more than six panels.

Try it yourself

The fixed version removes the panel and aborts its listener, so the demo keeps no detached panels.

Try it with the real Chrome Memory panel: press the leaky button a few times, collect garbage, and take snapshots. Then let go of the kept panels and try the fixed button.

Allocation timeline, sampling, and memory graphs

MEASURE

A heap snapshot answers: what is in memory now? The allocation tools answer: when was it made? To use Allocations on timeline:

  1. Start recording.
  2. Do the action.
  3. Stop recording, and select a time range.

The timeline shows bars of new objects:

  • Blue bars are objects still in memory at the end of the recording.
  • Gray bars are objects that were made and later freed.

When you select a constructor, the Retainers pane shows what keeps its objects in memory.

Allocation sampling checks only some of the new objects, so it is less exact. In return, it slows the page down less. It also groups memory by the chain of function calls that made it. Use it for dashboards that run a long time, or for heavy actions.

For a quick trend, open Performance monitor and watch the JS heap size while you repeat the action. Or record in the Performance panel with the Memory checkbox on. Then the heap growth lines up with scripts, drawing, and events on the same timeline.

Browser memory APIs give hints, not leak testsJavaScript
// Chrome exposes this non-standard property in some contexts.console.log("memory" in performance); // This newer measurement requires cross-origin isolation headers.if (crossOriginIsolated && "measureUserAgentSpecificMemory" in performance) {  const report = await performance.measureUserAgentSpecificMemory();  console.log(report.bytes);} else {  console.log("needs cross-origin isolation");}

performance.memory is not part of any web standard. Only Chrome has it. The newer measureUserAgentSpecificMemory() needs cross-origin isolation: a security setup that the server turns on with special headers. So use these for careful checks you control. Don't build app logic that expects them in every browser.

Node.js heap snapshots

NODE

Node.js runs JavaScript outside the browser. It uses V8, the same engine as Chrome, so heap snapshots work the same way. To look at a Node program on your own computer:

  1. Start it with the --inspect flag.
  2. Open chrome://inspect in Chrome.
  3. Choose your Node program, and use the Memory panel.

Be careful on a live server. While Node takes a snapshot, it stops running your other code, and it may need a lot of extra memory. So keep the way to start a snapshot private, and use it only where a crash would do no harm.

Inspect a Node program from ChromeShell
node --inspect server.js
# Open chrome://inspect, choose the Node target, then use the Memory panel.
Check that the V8 heap functions existJavaScript
import v8 from "node:v8";console.log(typeof v8.getHeapStatistics);console.log(typeof v8.writeHeapSnapshot);console.log(v8.getHeapStatistics().heap_size_limit > 0);

In this lesson's tests on Node 22, the check prints function, function, and true. To write a real snapshot, call v8.writeHeapSnapshot() from a safe, private place in your code. Then open the .heapsnapshot file in Chrome DevTools.

Write a heap snapshot from Node codeJavaScript
import { writeHeapSnapshot } from "node:v8"; // Use a private diagnostics path and protect whatever trigger calls this.const file = writeHeapSnapshot("./diagnostics/app.heapsnapshot");console.log(file);

Node can also write snapshots for you. The --heapsnapshot-near-heap-limit=3 flag tells it to write up to three snapshots as the heap gets close to its size limit, before an out-of-memory crash.

Write snapshots when memory is almost fullShell
node --heapsnapshot-near-heap-limit=3 server.js
# Node writes up to three snapshots as the heap approaches its limit.

Leak-hunting steps you can repeat

CHECKLIST

Hunt a leak like any other bug, with steps you can repeat. Don't guess. The advanced debugging lesson uses the same steps for breakpoints and traces: make the bug happen every time, remove other noise, look, explain, fix, and check.

  1. Reproduce: Make the leak happen with the smallest action you can repeat, such as opening and closing a panel.
  2. Measure: Use Performance monitor or the Memory checkbox in the Performance panel. Check whether the JS heap keeps rising after garbage collection.
  3. Isolate: Turn off other work, reload, do the action once as a warm-up, then repeat only the action you suspect.
  4. Compare: After the warm-up, take Snapshot 1. Do the action once and take Snapshot 2. Repeat the action, take Snapshot 3, then compare Snapshot 2 with Snapshot 3.
  5. Follow retainers: Open a constructor row that grew, pick one object, and read Retainers until you reach a root you own.
  6. Fix and verify: Remove listeners, abort signals, clear timers, limit caches, or use WeakMap or WeakSet when the link should not keep the object. Then compare again.
Memory tests must not promise too much

A regression test checks that an old bug doesn't come back. For a leak, it can:

  • run in its own Node process with --expose-gc, a flag that lets the test start garbage collection;
  • count objects in the heap;
  • use FinalizationRegistry as a hint that an object was freed, with no promise of when.

Never test for the exact moment garbage collection runs in a normal app. Repeat the test, run it in its own process, and fail only on strong evidence, such as an object count that keeps growing.

A careful leak test sketchJavaScript
// Run in a dedicated Node process, not in a normal browser page.// node --expose-gc leak-regression.test.jsconst registry = new FinalizationRegistry((label) => {  console.log("collected", label);}); (function mountAndUnmount() {  const marker = { label: "panel" };  registry.register(marker, "panel");})(); globalThis.gc?.();await new Promise((resolve) => setTimeout(resolve, 0));console.log("test can only use this as a best-effort signal");

Common misconceptions

Each row shows a wrong idea and what is really true.

Wrong ideas about memory leaks
Wrong ideaWhat is true
A heap snapshot is a picture of all memory.It is a graph of the JavaScript objects and page elements you can reach, starting from roots and following links.
The object with the biggest shallow size is always the leak.A small object with a big retained size can keep much more memory.
Detached means freed.Detached only means removed from the page. JavaScript can still point at the node and everything inside it.
One snapshot proves a leak.You need an action you can repeat, a comparison, retainers, a fix, and a new comparison.
performance.memory works the same in every browser.It is not part of any standard. Treat it as a Chrome-only hint, not as app logic.
Leak clue or normal memory use?
  • The `DetachedHTMLDivElement` count grows by the same amount after every page change.
  • A cache for code coloring appears only the first time a page opens.
  • The DevTools console keeps an object you selected.
  • A `Window` resize listener keeps the rows array of a page that is closed.
  • Blue bars stay on the allocation timeline after a repeated action.
  • The heap size goes up and down while you type in a search box.
Try it yourself
0 of 6 correct

Sort each thing you might see: is it worth looking into, usually normal, or caused by the tools or the test?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Practice: inspect, compare, fix, check

5 EXERCISES
Exercise 1 · Warm-upPredict the retained size

The lesson provides the heap helpers. What number does this print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const sizes = computeRetainedSizes(createLeakSnapshot(1));
console.log(sizes["panel:1"]);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeRead the retainer path

    Type the exact path that the code prints.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const path = findRetainerPath(createLeakSnapshot(1), "button:1");
    console.log(path.join(" -> "));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeRead the comparison delta

      How many extra detached panels does the comparison show?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const before = createLeakSnapshot(1);
      const after = createLeakSnapshot(5);
      const detached = diffConstructorCounts(before, after)
        .find((row) => row.name === "DetachedHTMLDivElement");
      console.log(detached.delta);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFix the root that keeps the panel

        Name one way to clean up, so the listener no longer keeps the panel.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        function mountPanel() {
          const panel = document.createElement("section");
          const onResize = () => panel.textContent.length;
          window.addEventListener("resize", onResize);
          panel.remove();
          // What cleanup should run here?
        }

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCheck the fix

          After you change the code, which DevTools step proves that the leak stopped growing?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          // After fixing the listener and the cache, which DevTools step proves it worked?
          // Type the step, not code.

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: finding memory leaks

            8 QUESTIONS
            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What is a heap snapshot in the Memory panel?

              Choose an answer to see the explanation.

            2. Question 2 of 8Which Memory panel tool slows the page down least during a long recording?

              Choose an answer to see the explanation.

            3. Question 3 of 8What does the retained-size code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const sizes = computeRetainedSizes(createLeakSnapshot(1));
              console.log(sizes["panel:1"]);

              Choose an answer to see the explanation.

            4. Question 4 of 8Why do you compare Snapshot 2 with Snapshot 3 in the three-snapshot technique?

              Choose an answer to see the explanation.

            5. Question 5 of 8What does this comparison code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const before = createLeakSnapshot(1);
              const after = createLeakSnapshot(5);
              const detached = diffConstructorCounts(before, after)
                .find((row) => row.name === "DetachedHTMLDivElement");
              console.log(detached.delta);

              Choose an answer to see the explanation.

            6. Question 6 of 8The Retainers pane for an object shows Window → resize listener → closure → rows. What is the likely fix?

              Choose an answer to see the explanation.

            7. Question 7 of 8What is true about detached DOM nodes?

              Choose an answer to see the explanation.

            8. Question 8 of 8Which Node.js option writes snapshots just before an out-of-memory crash?

              Choose an answer to see the explanation.

            Key takeaways

            • The Memory panel has heap snapshots, an allocation timeline, allocation sampling, and a detached elements tool.
            • A heap snapshot is a graph of objects joined by links. Retained size counts what only one object reaches. The Retainers pane shows why an object is still in memory.
            • In the three-snapshot technique, Snapshot 1 checks the warm-up, Snapshot 2 is the starting point, and Snapshot 3 shows growth from repeating the action.
            • A detached DOM node leaks only while JavaScript still points at it. Follow the retainer chain to the array, listener, closure, cache, or console that holds it.
            • Fix the root that holds the memory. Then run the same comparison again, and add a careful regression test.

            One sentence to keep:
            To find a memory leak, don't guess from one heap number: compare repeated actions, and follow the retainers back to the code that should clean up.

            Next, the course goes deeper into performance and how the engine works. Come back to these steps any time a page, a dashboard, or a long session keeps getting heavier.

            CompleteFrontend Clear concepts. Working examples.