Uh oh!
There was an error while loading. Please reload this page.
Do not reuse PersistentContextPtr instances - #261
Conversation
Overall package sizeSelf size: 1.77 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | source-map | 0.7.6 | 185.63 kB | 185.63 kB | | pprof-format | 2.2.1 | 163.06 kB | 163.06 kB | | p-limit | 3.1.0 | 7.75 kB | 13.78 kB | | delay | 5.0.0 | 11.17 kB | 11.17 kB | | node-gyp-build | 3.9.0 | 8.81 kB | 8.81 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
ee05b8d to
42bb2d4Compare42bb2d4 to
e9cd659Comparee9cd659 to
f1af6c9Compare
BridgeAR
left a comment
There was a problem hiding this comment.
While we can try this, I do not believe this is the actual cause of the report
IlyasShabi
commented
Feb 10, 2026
@BridgeAR Actually I think it's good to remove Note: I see that |
Uh oh!
There was an error while loading. Please reload this page.
Review follow-up on #385. v8::Persistent has no destruction behaviour: the handle leaks unless every path clears it by hand. v8::Global releases it in its own destructor, and is what every other handle in this file already uses — ContextPtr, cpedKey_, wrapObjectTemplate_, jsArray_. The Persistent introduced in #385 was the odd one out. Nothing was leaking in practice, since ~PersistentContextPtr always reset the handle explicitly, but relying on that is exactly the footgun the V8 docs warn about. Switching to Global makes the release structural, so the explicit Reset goes away with it. Historically the manual handle was justified: before #261 removed instance reuse, PersistentContextPtr recycled itself through a freelist and needed ClearWeak/Reset to unregister and re-register the same object. With reuse gone a handle lives exactly as long as its PCP, so there is nothing left for Persistent's manual semantics to buy. Also correct the destructor comment. It claimed the reset was "a no-op when we got here from WeakCallback itself", which is backwards: V8 requires a weak callback to reset the handle, so that path is precisely where the release is load-bearing. Verified on Node 20, 24 and 26 — the last is where AsyncContextFrame is on by default and PCPs are actually created. 163 passing, ASAN exit 0 with no leaks and no aborts on 20 and 24.
Review follow-up on #385. v8::Persistent has no destruction behaviour: the handle leaks unless every path clears it by hand. v8::Global releases it in its own destructor, and is what every other handle in this file already uses — ContextPtr, cpedKey_, wrapObjectTemplate_, jsArray_. The Persistent introduced in #385 was the odd one out. Nothing was leaking in practice, since ~PersistentContextPtr always reset the handle explicitly, but relying on that is exactly the footgun the V8 docs warn about. Switching to Global makes the release structural, so the explicit Reset goes away with it. Historically the manual handle was justified: before #261 removed instance reuse, PersistentContextPtr recycled itself through a freelist and needed ClearWeak/Reset to unregister and re-register the same object. With reuse gone a handle lives exactly as long as its PCP, so there is nothing left for Persistent's manual semantics to buy. Verified on Node 20, 24 and 26 — the last is where AsyncContextFrame is on by default and PCPs are actually created. 163 passing, ASAN exit 0 with no leaks and no aborts on 20 and 24.
Review follow-up on #385. v8::Persistent has no destruction behaviour: the handle leaks unless every path clears it by hand. v8::Global releases it in its own destructor, and is what every other handle in this file already uses — ContextPtr, cpedKey_, wrapObjectTemplate_, jsArray_. The Persistent introduced in #385 was the odd one out. Nothing was leaking in practice, since ~PersistentContextPtr always reset the handle explicitly, but relying on that is exactly the footgun the V8 docs warn about. Switching to Global makes the release structural, so the explicit Reset goes away with it. Historically the manual handle was justified: before #261 removed instance reuse, PersistentContextPtr recycled itself through a freelist and needed ClearWeak/Reset to unregister and re-register the same object. With reuse gone a handle lives exactly as long as its PCP, so there is nothing left for Persistent's manual semantics to buy. Verified on Node 20, 24 and 26 — the last is where AsyncContextFrame is on by default and PCPs are actually created. 163 passing, ASAN exit 0 with no leaks and no aborts on 20 and 24. (cherry picked from commit 9c00d10)
What does this PR do?:
Removes caching/reuse of PersistentContextPtr instances.
Motivation:
This was always a speculative attempt to reduce native memory fragmentation. The standard library can be expected to do some of this itself, so we should not be doing this, especially since it is also removed in #255 and it seems to be causing issues (see #7355).
Additional Notes:
#255 also contains this, but we can roll it out as a quicker measure to try to eliminate the memory issue. An advantage of rolling this out separately is that #255 requires some changes in dd-trace-js too, while this as a standalone change does not.