Repository navigation
Memory leak if unhandled promise rejection mode is set to warn #47158
Description
Activity
If I am right about the above memory leak and there is not any PR available for fixing this please let me know I can create a PR to fix this.
This is indeed very likely a duplicate of #43655. I would suggest to close it as such.
maybeUnhandledPromisesis a WeakMap. Entries get collected by the garbage collector eventually but the way V8 handles promises internally keeps them alive for longer than one would intuitively expect. There's no easy fix for that, unfortunately.Reacted by Benjamin GruenbaumBut why don't we just remove the promise entry from the
maybeUnhandledPromisesafter completingprocessPromiseRejectionfunction? If we manually delete the promise from the WeakMap after handling the promise rejection there won't be any memory leak.It is. It's done a few lines below the code you linked to.
Umm. Can you give me a permalink? I am not able to find that.
Sorry, it's a few lines up, not down:
node/lib/internal/process/promises.js
Line 168 in 2984cc3
maybeUnhandledPromises.delete(promise);
At themaybeUnhandledPromises.get(promise)you linked to, I suspect it's not safe yet to remove it from the map.Reacted by Benjamin GruenbaumBut heapprofiler doesn't reach that function. see this function
never got executed.node/lib/internal/process/promises.js
Line 165 in 2984cc3
function handledRejection(promise) { I don't want to turn this issue into a line-by-line walkthrough of promises.js. I'll explain this one thing but after that you're on your own, okay?
-
unhandledRejection() is called when a promise without a .catch handler is rejected
-
handledRejection() is called when a promise without a .catch handler is rejected and then adds a .catch handler
That is, unhandledRejection() and handledRejection() can get called for the same promise, in that order.
That's why unhandledRejection() stores the promise and its associated state in the WeakMap, because handledRejection() may need it later.
Reacted by Benjamin Gruenbaum-
Okay. Thanks for this clarification. Then I think this is already being discussed in #43655.
I'm glad you agree. I'll close this out as a duplicate then.
Reacted by Benjamin Gruenbaum- addedduplicateIssues and PRs that are duplicates of other issues or PRs.Issues and PRs that are duplicates of other issues or PRs.
on Mar 21, 2023

Version
18.14.0
Platform
Darwin 2.2.0 Darwin Kernel Version 22.2.0: Fri Nov 11 02:04:44 PST 2022; root:xnu-8792.61.2~4/RELEASE_ARM64_T8103 arm64
Subsystem
promise
What steps will reproduce the bug?
if you run this program using
node --unhandled-rejections=warn temp.jsyou will see the diff log of memwatch where memory doesn't get free. Now if you changetask(1000)totask(1000).catch(e=>{})the memory will get free.How often does it reproduce? Is there a required condition?
100% reproducible with node option set to
unhandled-rejections=warn.What is the expected behavior? Why is that the expected behavior?
The memory should get free after printing the warnings on the console.
What do you see instead?
Memory is not getting freed.
Additional information
I ran a clinic js heap profiler and found out that it comes to this function
node/lib/internal/process/promises.js
Line 221 in ad2c3c0
node/lib/internal/process/promises.js
Line 187 in ad2c3c0
The interesting thing is inside
processPromiseRejectionwe do get the promise frommaybeUnhandledPromiseMap(node/lib/internal/process/promises.js
Line 234 in ad2c3c0