Version
1.61.0
Steps to reproduce
Minimal reproduction example:
import { chromium } from 'playwright';
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent(`<button id="btn">click</button>`);
// 1) A normal locator action -> the button becomes retained
await page.locator('#btn').click();
// 2) Track the button with a WeakRef, then detach it from the DOM.
await page.evaluate(() => {
globalThis.ref = new WeakRef(document.getElementById('btn'));
document.getElementById('btn').remove();
});
// 3) Request garbage collection, wait
await page.requestGC();
await page.waitForTimeout(1000);
await page.requestGC();
// 4) Check if button was collected
console.log('collected after detach:', await page.evaluate(() => globalThis.ref.deref() === undefined)); // => false (leaked)
// 5) Perform any action on another element -> removes button retention
await page.locator('body').hover();
// 6) Request garbage collection again, check again
await page.requestGC();
console.log('collected after extra action:', await page.evaluate(() => globalThis.ref.deref() === undefined)); // => true
await browser.close();
})();
This script was based on example from Page.requestGC() documentation.
Expected behavior
Locator actions should not retain detached DOM nodes in memory.
Actual behavior
Somehow last target dom element gets retained and cannot be garbage collected. This can affect memory leak detection. Same manual actions outside of playwright environment do not produce such problem.
Additional context
For some reason, dom node leak does not happen if you run reproduction example in headed mode, but it does in my production app.
I'm attaching a heap snapshot captured from reproduction example headless via CDP (HeapProfiler.takeHeapSnapshot) after first GC attempt and WeakRef.deref(), but i couldnt find anything useful in it.
pw.example.heapsnapshot.json
However, I suspect playwright's InjectedScript.markTargetElements might be involved.
I am willing to investigate further, and produce a PR if needed.
Environment
System:
OS: Windows 11 10.0.26100
CPU: (20) x64 13th Gen Intel(R) Core(TM) i7-13700H
Memory: 15.01 GB / 47.53 GB
Binaries:
Node: 24.16.0 - C:\Program Files\nodejs\node.EXE
npm: 11.13.0 - C:\Program Files\nodejs\npm.CMD
npmPackages:
@playwright/test: ^1.61.0 => 1.61.0
Version
1.61.0
Steps to reproduce
Minimal reproduction example:
This script was based on example from Page.requestGC() documentation.
Expected behavior
Locator actions should not retain detached DOM nodes in memory.
Actual behavior
Somehow last target dom element gets retained and cannot be garbage collected. This can affect memory leak detection. Same manual actions outside of playwright environment do not produce such problem.
Additional context
For some reason, dom node leak does not happen if you run reproduction example in headed mode, but it does in my production app.
I'm attaching a heap snapshot captured from reproduction example headless via CDP (
HeapProfiler.takeHeapSnapshot) after first GC attempt andWeakRef.deref(), but i couldnt find anything useful in it.pw.example.heapsnapshot.json
However, I suspect playwright's
InjectedScript.markTargetElementsmight be involved.I am willing to investigate further, and produce a PR if needed.
Environment
System: OS: Windows 11 10.0.26100 CPU: (20) x64 13th Gen Intel(R) Core(TM) i7-13700H Memory: 15.01 GB / 47.53 GB Binaries: Node: 24.16.0 - C:\Program Files\nodejs\node.EXE npm: 11.13.0 - C:\Program Files\nodejs\npm.CMD npmPackages: @playwright/test: ^1.61.0 => 1.61.0