Closed
Description
Bug Description
The memory of the Node process grows infinitely, ultimately resulting in an OOM error. Memory profiling revealed a memory leak related to AbortController.
Similar to #54614,but the difference is that if AbortSignal.any does not have an event listener added, AbortSignal.any will eventually be released and there will be no memory leak.
I am using fetch API with AbortController/AbortSignal in Node.js(not undici directly).
Sorry, I misremembered, I was using node:http+signal.
Reproducible By
const formatMemoryUsage = (data) =>
`${Math.round((data / 1024 / 1024) * 100) / 100} MB`;
// let i = 0;
// const registry = new FinalizationRegistry((heldValue) => {
// i += 1;
// // console.debug("[registry]", heldValue);
// });
let memoryData = process.memoryUsage();
console.log("Mem before loop", formatMemoryUsage(memoryData.rss));
const run = () => {
for (let i = 0; i < 100_000; i++) {
const abortController = new AbortController();
const signal = abortController.signal;
const composedSignal = AbortSignal.any([signal]);
composedSignal.addEventListener("abort", () => {
// It looks like the closure reference in the callback function causes the signal to not be released?
// console.log(signal); // Uncommenting this will aggravate the memory leak
});
// registry.register(signal, "signal");
}
};
setImmediate(run);
setInterval(() => {
if (typeof global.gc == "function") {
global.gc(true);
} else {
console.warn("global.gc unavailable, need '--expose-gc' flag");
}
const memoryData = process.memoryUsage();
console.log("Mem after 100K iteration", formatMemoryUsage(memoryData.rss));
// console.log(i, "signals released");
}, 1000);
/app # node --trace-gc --expose-gc test3.js
Mem before loop 43.78 MB
[380:0x7f3bd3df6000] 44 ms: Scavenge 3.9 (5.2) -> 3.5 (6.2) MB, pooled: 0 MB, 0.74 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 45 ms: Scavenge 3.9 (6.2) -> 3.8 (6.7) MB, pooled: 0 MB, 0.62 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 47 ms: Scavenge 4.5 (6.7) -> 4.3 (9.2) MB, pooled: 0 MB, 0.74 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 50 ms: Scavenge 5.9 (9.2) -> 5.4 (9.7) MB, pooled: 0 MB, 0.99 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 53 ms: Scavenge 6.3 (9.7) -> 6.0 (14.7) MB, pooled: 0 MB, 1.20 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 57 ms: Scavenge 9.5 (14.8) -> 8.4 (15.6) MB, pooled: 0 MB, 1.41 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 65 ms: Scavenge 10.6 (16.1) -> 10.0 (26.1) MB, pooled: 0 MB, 3.05 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 80 ms: Scavenge 16.8 (26.1) -> 15.2 (27.6) MB, pooled: 0 MB, 2.62 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 88 ms: Scavenge 18.9 (28.5) -> 18.3 (49.8) MB, pooled: 0 MB, 3.92 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 104 ms: Scavenge 33.7 (51.7) -> 31.0 (53.7) MB, pooled: 0 MB, 4.48 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 115 ms: Scavenge 35.8 (53.7) -> 34.8 (64.9) MB, pooled: 0 MB, 7.48 / 0.00 ms (average mu = 1.000, current mu = 1.000) allocation failure;
[380:0x7f3bd3df6000] 127 ms: Mark-Compact 41.7 (64.9) -> 38.2 (73.8) MB, pooled: 0 MB, 2.82 / 0.00 ms (+ 0.4 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 12 ms) (average mu = 0.977, current mu = 0.977) finalize incremental marking via stack guard; GC in old space requested
[380:0x7f3bd3df6000] 148 ms: Scavenge 57.7 (77.6) -> 54.6 (77.6) MB, pooled: 0 MB, 4.69 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 159 ms: Scavenge 57.7 (77.6) -> 57.1 (89.6) MB, pooled: 0 MB, 7.86 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 172 ms: Scavenge 70.3 (89.6) -> 67.6 (91.6) MB, pooled: 0 MB, 3.94 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 182 ms: Scavenge 72.7 (91.6) -> 71.6 (102.1) MB, pooled: 0 MB, 5.92 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 194 ms: Scavenge 83.2 (102.1) -> 80.9 (106.3) MB, pooled: 0 MB, 4.03 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 204 ms: Scavenge 87.3 (106.3) -> 86.0 (115.6) MB, pooled: 0 MB, 4.54 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 216 ms: Scavenge 96.5 (115.6) -> 94.4 (120.8) MB, pooled: 0 MB, 4.04 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 235 ms: Scavenge 109.2 (128.3) -> 107.7 (137.1) MB, pooled: 0 MB, 5.21 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 255 ms: Scavenge (interleaved) 117.6 (137.1) -> 115.6 (143.3) MB, pooled: 0 MB, 6.53 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 273 ms: Scavenge (interleaved) 123.4 (143.3) -> 121.8 (151.6) MB, pooled: 0 MB, 7.72 / 0.00 ms (average mu = 0.977, current mu = 0.977) allocation failure;
[380:0x7f3bd3df6000] 279 ms: Mark-Compact 123.1 (151.6) -> 117.2 (152.7) MB, pooled: 0 MB, 3.91 / 0.00 ms (+ 0.7 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 44 ms) (average mu = 0.975, current mu = 0.973) finalize incremental marking via stack guard; GC in old space requested
[380:0x7f3bd3df6000] 296 ms: Scavenge 132.9 (152.7) -> 129.8 (152.7) MB, pooled: 0 MB, 4.28 / 0.00 ms (average mu = 0.975, current mu = 0.973) allocation failure;
[380:0x7f3bd3df6000] 308 ms: Scavenge 132.9 (152.7) -> 132.2 (165.4) MB, pooled: 0 MB, 7.98 / 0.00 ms (average mu = 0.975, current mu = 0.973) allocation failure;
[380:0x7f3bd3df6000] 322 ms: Scavenge 145.4 (165.4) -> 142.8 (167.9) MB, pooled: 0 MB, 4.07 / 0.00 ms (average mu = 0.975, current mu = 0.973) allocation failure;
[380:0x7f3bd3df6000] 333 ms: Scavenge 147.9 (167.9) -> 146.9 (178.7) MB, pooled: 0 MB, 6.16 / 0.00 ms (average mu = 0.975, current mu = 0.973) allocation failure;
[380:0x7f3bd3df6000] 1047 ms: Scavenge 149.1 (178.7) -> 148.6 (182.7) MB, pooled: 0 MB, 6.11 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 232.23 MB
[380:0x7f3bd3df6000] 2046 ms: Scavenge 148.7 (182.7) -> 148.6 (184.7) MB, pooled: 0 MB, 3.56 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.25 MB
[380:0x7f3bd3df6000] 3043 ms: Scavenge 148.6 (184.7) -> 148.6 (184.7) MB, pooled: 0 MB, 0.95 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.24 MB
[380:0x7f3bd3df6000] 4045 ms: Scavenge 148.6 (184.7) -> 148.6 (184.7) MB, pooled: 0 MB, 0.76 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.25 MB
[380:0x7f3bd3df6000] 5047 ms: Scavenge 148.6 (184.7) -> 148.6 (184.7) MB, pooled: 0 MB, 1.83 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.31 MB
[380:0x7f3bd3df6000] 6047 ms: Scavenge 148.6 (184.7) -> 148.7 (184.7) MB, pooled: 0 MB, 1.13 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.3 MB
[380:0x7f3bd3df6000] 7047 ms: Scavenge 148.7 (184.7) -> 148.6 (184.7) MB, pooled: 0 MB, 0.78 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.29 MB
[380:0x7f3bd3df6000] 8050 ms: Scavenge 148.7 (184.7) -> 148.7 (153.7) MB, pooled: 31 MB, 1.55 / 0.00 ms (average mu = 0.975, current mu = 0.973) testing;
Mem after 100K iteration 234.29 MB
[380:0x7f3bd3df6000] 8463 ms: Mark-Compact (reduce) 148.9 (153.7) -> 82.7 (143.4) MB, pooled: 0 MB, 10.03 / 0.00 ms (+ 14.2 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 27 ms) (average mu = 0.997, current mu = 0.997) finalize incremental marking via task; GC in old space requested
[380:0x7f3bd3df6000] 9051 ms: Scavenge 82.7 (143.4) -> 82.7 (144.4) MB, pooled: 0 MB, 1.57 / 0.00 ms (average mu = 0.997, current mu = 0.997) testing;
Mem after 100K iteration 148.13 MB
[380:0x7f3bd3df6000] 9094 ms: Mark-Compact (reduce) 82.7 (144.4) -> 82.7 (101.4) MB, pooled: 0 MB, 9.94 / 0.00 ms (+ 16.9 ms in 0 steps since start of marking, biggest step 0.0 ms, walltime since start of marking 30 ms) (average mu = 0.992, current mu = 0.958) finalize incremental marking via task; GC in old space requested
[380:0x7f3bd3df6000] 10051 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 1.02 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.08 MB
[380:0x7f3bd3df6000] 11051 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.37 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.08 MB
[380:0x7f3bd3df6000] 12052 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.42 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 13053 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.84 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 14054 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.42 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 15056 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.74 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 16057 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.76 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 17059 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.63 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 18060 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.59 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 19061 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.41 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 20062 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.64 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 21063 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.81 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
[380:0x7f3bd3df6000] 22063 ms: Scavenge 82.7 (101.4) -> 82.7 (101.4) MB, pooled: 1 MB, 0.43 / 0.00 ms (average mu = 0.992, current mu = 0.958) testing;
Mem after 100K iteration 141.09 MB
/app #
Expected Behavior
All AbortController/AbortSignal should be destroyed.
Logs & Screenshots
Environment
Node: v22.3.0
Docker Image: node:apline