Skip to content

AbortSignal.timeout() + util.aborted() causes unbounded memory growth #33125

@kevgeoleo

Description

@kevgeoleo

Version: Deno 2.7.9

Hi,

i would like to report a behavior in Deno when running the below snippet:

"use strict";

const { setImmediate } = require("timers/promises");
const { aborted } = require("util");

const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`;

function logMemory() {
  const memoryData = process.memoryUsage();

  const memoryUsage = {
    rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`,
    heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`,
    heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`,
    external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`,
  };

  console.log(memoryUsage);
}

(async () => {
  while (true) {
    for (let i = 0; i < 10000; i++) {
      function lis() {}

      const timeoutSignal = AbortSignal.timeout(1_000_000_000);
      // 1. Add a regular listener
      timeoutSignal.addEventListener("abort", lis);
      // 2. Wrap with util.aborted (adds a weak listener internally)
      aborted(timeoutSignal, {});
      // 3. Remove the regular listener
      timeoutSignal.removeEventListener("abort", lis);
    }

    await setImmediate();
    if (global.gc) global.gc();
  }
})().catch(console.error);

setInterval(() => {
  logMemory();
}, 1000);

What is the expected behavior?

Memory usage remains constant

root@KContainer:~/48951# node --expose-gc leak-repro.cjs
{
  rss: '127.44 MB -> Resident Set Size - total memory allocated for the process execution',
  heapTotal: '163.07 MB -> total size of the allocated heap',
  heapUsed: '57.49 MB -> actual memory used during the execution',
  external: '1.2 MB -> V8 external memory'
}
{
  rss: '129.48 MB -> Resident Set Size - total memory allocated for the process execution',
  heapTotal: '163.32 MB -> total size of the allocated heap',
  heapUsed: '50.72 MB -> actual memory used during the execution',
  external: '1.47 MB -> V8 external memory'
}
{
  rss: '132.06 MB -> Resident Set Size - total memory allocated for the process execution',
  heapTotal: '164.32 MB -> total size of the allocated heap',
  heapUsed: '57.7 MB -> actual memory used during the execution',
  external: '1.47 MB -> V8 external memory'
}
{
  rss: '131.7 MB -> Resident Set Size - total memory allocated for the process execution',
  heapTotal: '164.94 MB -> total size of the allocated heap',
  heapUsed: '57.12 MB -> actual memory used during the execution',
  external: '1.47 MB -> V8 external memory'
}
{
  rss: '130.69 MB -> Resident Set Size - total memory allocated for the process execution',
  heapTotal: '163.07 MB -> total size of the allocated heap',
  heapUsed: '50.74 MB -> actual memory used during the execution',
  external: '1.47 MB -> V8 external memory'
}

What do you see instead?

Memory usage keeps increasing eventually resulting in Segfault

root@KContainer:~/48951# deno run --v8-flags=--expose-gc leak-repro.cjs
{
  rss: "180.45 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "126.25 MB -> total size of the allocated heap",
  heapUsed: "110.42 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
}
{
  rss: "232.1 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "174.75 MB -> total size of the allocated heap",
  heapUsed: "157.79 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
}
{
  rss: "269.84 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "211.5 MB -> total size of the allocated heap",
  heapUsed: "195.63 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
}
{
  rss: "311.56 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "248.5 MB -> total size of the allocated heap",
  heapUsed: "233.49 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
}
{
  rss: "339.15 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "276 MB -> total size of the allocated heap",
  heapUsed: "261.87 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
}
{
  rss: "367.55 MB -> Resident Set Size - total memory allocated for the process execution",
  heapTotal: "303.75 MB -> total size of the allocated heap",
  heapUsed: "290.25 MB -> actual memory used during the execution",
  external: "3.4 MB -> V8 external memory"
.
.
.
<--- Last few GCs --->

[3896:0x55d0ccefb000]    37144 ms: Scavenge (during sweeping) 4073.8 (4086.2) -> 4072.0 (4090.8) MB, pooled: 0.0 MB, 12.97 / 0.00 ms (average mu = 0.128, current mu = 0.122) allocation failure;
[3896:0x55d0ccefb000]    43093 ms: Incremental Mark-Compact (reduce) 4080.9 (4094.2) -> 4077.1 (4082.2) MB, pooled: 0.0 MB, 5747.44 / 0.00 ms (+ 8.3 ms in 128 steps since start of marking, biggest step 4.2 ms, walltime since start of marking 5805 ms) (ave


#
# Fatal JavaScript out of memory: Ineffective mark-compacts near heap limit
#
==== C stack trace ===============================

    deno(+0x306648e) [0x55d0c675648e]
    deno(+0x306544b) [0x55d0c675544b]
    deno(+0x30676a8) [0x55d0c67576a8]
    deno(+0x311dce5) [0x55d0c680dce5]
    deno(+0x3308d17) [0x55d0c69f8d17]
    deno(+0x3307d53) [0x55d0c69f7d53]
    deno(+0x3306ac5) [0x55d0c69f6ac5]
    deno(+0x3353e76) [0x55d0c6a43e76]
    deno(+0x558266c) [0x55d0c8c7266c]
    deno(+0x55823b3) [0x55d0c8c723b3]
    deno(+0x679349d) [0x55d0c9e8349d]
    deno(+0x6794df7) [0x55d0c9e84df7]
    deno(+0x6790e0f) [0x55d0c9e80e0f]
    deno(+0x67aef74) [0x55d0c9e9ef74]
    deno(+0x67ac8da) [0x55d0c9e9c8da]
    deno(+0x7059052) [0x55d0ca749052]
    deno(+0x4a52443) [0x55d0c8142443]
    deno(+0x4a52f14) [0x55d0c8142f14]
    deno(+0x4a4f12a) [0x55d0c813f12a]
    /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7ff0a749ad90]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7ff0a749ae40]
    deno(+0x2e3002a) [0x55d0c652002a]
Trace/breakpoint trap

}```

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions