Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A high memory reading alone does not prove a leak. Repeat the same browser-automation workflow, measure memory at equivalent points after cleanup, then inspect the process and object references that keep growing. Chrome DevTools heap snapshots help investigate page JavaScript; Node.js V8 statistics and snapshots help inspect the runner. Neither view, by itself, accounts for every browser process or native allocation.

1. Reproduce the growth with a repeatable workload

Start with one test or automation cycle that reliably shows the problem. Keep the browser and automation-library versions, data volume, and worker count stable while investigating. Record memory at the same points each time—for example, after setup, after the repeated action, and after teardown.

Run several equivalent cycles and allow the workflow’s usual cleanup and settling period before measuring. A temporary peak during a page load or a single high reading is not enough to establish retention. Do not compare unrelated runs with different workloads or concurrency and treat the difference as a leak.

2. Find which process and memory domain are growing

Browser automation spans more than one memory domain. First determine whether the apparent growth is in the page’s JavaScript heap, the Node.js runner’s V8 heap, browser subprocess or native memory, or total process resident memory (RSS). A metric from one domain does not describe all the others.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When the page’s JavaScript heap is growing

Open Chrome DevTools’ Memory panel and take heap snapshots around the same workflow. Snapshots represent reachable JavaScript objects; they are useful for finding objects that remain referenced, but they are not a complete measure of browser or operating-system memory. Chrome’s Memory panel overview describes the available memory tools.

When the Node.js automation runner is growing

Sample V8 heap statistics and process memory separately. Node’s V8 statistics include used_heap_size, total_heap_size, and external_memory; process RSS includes memory beyond the V8 heap. If RSS rises while V8 heap use does not show comparable growth, investigate native allocations, browser subprocesses, or other process memory rather than assuming JavaScript objects are leaking. See the Node.js V8 API documentation for the available statistics and snapshot API.

3. Compare equivalent heap snapshots

  1. Capture a baseline after the workflow is in a consistent starting state.
  2. Run the same action or cycle several times, using the same inputs and cleanup.
  3. Allow the usual settling period, then capture another snapshot under equivalent conditions.
  4. In Chrome DevTools, use the Comparison view to examine what accumulated or was freed between snapshots. Inspect object counts and retaining paths, not just total size.
  5. Repeat the comparison. Look for a persistent increase in objects that should have been released, and identify the reference path that keeps them reachable.

Chrome documents that heap snapshot capture begins with garbage collection. Even so, snapshots are point-in-time views of reachable JavaScript objects, not a measurement of every native allocation or process. A credible leak signal is repeatable growth in objects that ought to be gone, with a retaining path that explains why they remain. Chrome’s heap snapshot guide explains Summary and Comparison views.

4. Trace retained objects to their owner

Page JavaScript and detached DOM

Search for detached DOM nodes and follow their retaining paths. Removing a node from the document does not make it collectible if JavaScript still holds a reference—for example, through a closure, global, collection, or event handler. Chrome’s guide to fixing memory problems describes this pattern and how to trace the reference keeping a node alive.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Automation lifecycle and repeated listeners

Check whether pages or explicitly created browser contexts live longer than intended, and whether listeners are registered repeatedly but never removed. Also inspect data structures that grow across iterations: arrays, logs, response bodies, screenshots, traces, or application caches. These are hypotheses to test in your own code, not a universal explanation for Playwright memory growth.

Playwright’s Page API documents page event listener operations. Its Browser API guidance describes explicitly closing contexts before the browser when graceful page closure and close events matter.

5. Fix the owner’s cleanup and validate the change

  • Release references when the code that owns them is finished; avoid retaining obsolete page objects or detached nodes.
  • Remove event listeners when their owner’s work is done.
  • Clear collections only when they are not intended to persist. Give intentional caches a documented bound and lifetime.
  • Close pages and explicitly created contexts as part of teardown. Arrange cleanup to run when a test fails as well as when it passes, using the project’s fixture or a finally path where appropriate.

Then repeat the original workload and compare the same observations. A fix is supported when the suspect retained-object growth stops and the relevant process’s memory trend stabilizes under equivalent conditions. Raising a memory limit may postpone failure, but it does not remove a retaining reference.

Node.js heap snapshots: plan for their cost

Node’s v8.writeHeapSnapshot() creates a snapshot that can be opened with tools such as Chrome DevTools. It captures one V8 isolate; worker-thread isolates need their own captures. Snapshot creation is synchronous and blocks the event loop. Node also warns that creating one needs memory about twice the heap size at capture time, so a constrained process can run out of memory while taking the snapshot. Capture deliberately, leave headroom, and avoid treating snapshot collection as a harmless production action. Details are in the Node.js V8 documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting misleading or incomplete signals

  • Memory rises during one action, then falls after cleanup: compare measurements after the same settling period across repeated cycles. A transient peak alone does not establish a leak.
  • Snapshot size grows, but the cause is unclear: use Comparison to find accumulating object types, then inspect retaining paths and detached nodes to locate the owner.
  • RSS rises while V8 heap statistics do not: do not label it a JavaScript-object leak on that evidence. Investigate external/native memory and browser subprocesses as separate scopes.
  • The issue appears only at higher worker counts: record worker count and compare equivalent runs at the same concurrency; do not attribute a peak-concurrency difference to a leak without repeatable evidence.
  • Taking a Node snapshot stalls or terminates the runner: snapshot generation blocks the event loop and needs substantial memory headroom. Capture in a controlled environment or with a safer memory margin.
  • Memory remains high after a fix: repeat the same cycle and inspect retained-object growth, not just whether the process immediately returns to its earlier RSS. The relevant signal is whether suspect retention continues under equivalent conditions.

Or skip the browser setup

If your task is to capture pages rather than diagnose their memory, ScreenshotNeo is a website screenshot API and MCP server. A single GET request can return a screenshot or PDF; it is not a replacement for heap profiling. For example, this cURL call saves a WebP screenshot of Stripe:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp — see the ScreenshotNeo API documentation.

ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, with no card required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does a heap snapshot show all browser memory?

No. It shows reachable JavaScript objects for the captured scope; browser subprocess and native memory require separate investigation.

Can worker threads share one Node.js heap snapshot?

No. A Node.js snapshot covers one V8 isolate, so worker-thread isolates require separate captures.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.