-
-
Notifications
You must be signed in to change notification settings - Fork 34.9k
Closed as not planned
Description
Version
v24.13.1&v25.6.1
Platform
uname -a --> (Linux tihi 6.17.0-14-generic #14~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 15 15:52:10 UTC 2 x86_64 x86_64 x86_64 GNU/Linux)
lsb_release -a -> (
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.4 LTS
Release: 24.04
Codename: noble
)
Subsystem
No response
What steps will reproduce the bug?
Here is a test file that demonstrates the issue.
// WeakRef Bug Report - Node.js WeakRef prevents object GC -
// Tested with only sleeps and no manual GC calls same results.
// WeakRef is not acting as a weak reference
// Tested on: Feb 17th 2026 - Node versions v24.13.1 & v25.6.1
// Result: FAILED on both versions
const SLEEP_TIME = 5
const registry = new FinalizationRegistry((heldValue) => {
console.log(`Cleaned up resources for: ${heldValue}`);
});
const test1 = async () => {
console.log("=== Test 1: WeakRef alone ===");
let obj1 = { data: 'test1' };
const obj1_ref = new WeakRef(obj1);
console.log('Before null:', obj1_ref.deref() ? 'EXISTS' : 'undefined');
obj1 = null;
global.gc();
await new Promise(resolve => {
setTimeout(() => {
console.log(`[Test 1] After ${SLEEP_TIME} s:`, obj1_ref.deref() ? 'Reference still EXISTS (BUG!)' : 'undefined (correct)');
resolve();
}, SLEEP_TIME*1000);
});
}
const test2 = () => {
console.log("\n=== Test 2: FinalizationRegistry alone ===");
let obj2 = { data: 'test2' };
registry.register(obj2, 'test2 object with no "weak" reference"');
obj2 = null;
global.gc();
}
const test3 = async () => {
console.log("\n=== Test 3: Both together ===");
let obj3 = { data: 'test3' };
let obj4 = { data: 'test3 no weak reference' };
const obj3_ref = new WeakRef(obj3);
registry.register(obj3, 'test3 with "weak" reference');
registry.register(obj4, 'test3 object with no "weak" reference');
obj3 = null;
obj4 = null;
global.gc();
await new Promise(resolve => {
setTimeout(() => {
console.log(`[Test 3] After ${SLEEP_TIME} s:`, obj3_ref.deref() ? 'Reference still EXISTS (BUG!)' : 'undefined (correct)');
resolve();
}, SLEEP_TIME*1000);
});
}
async function report() {
await test1()
test2()
await test3()
}
report()
### How often does it reproduce? Is there a required condition?
The bug reproduces each time. I tested the bug with different sleep timers, up to 240 seconds and there was no difference. WeakReference is preventing GC from working as expected.
### What is the expected behavior? Why is that the expected behavior?
WeakRef should not prevent garbage collection. Many libraries depend on this, including on-exit-leak-free.
### What do you see instead?
=== Test 1: WeakRef alone ===
Before null: EXISTS
[Test 1] After 5 s: Reference still EXISTS (BUG!)
=== Test 2: FinalizationRegistry alone ===
Cleaned up resources for: test2 object with no "weak" reference"
=== Test 3: Both together ===
Cleaned up resources for: test3 object with no "weak" reference
[Test 3] After 5 s: Reference still EXISTS (BUG!)
### Additional information
I tested this without --expose-gc flag with only sleeps too and the result was the same. Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels