node-addon-api icon indicating copy to clipboard operation
node-addon-api copied to clipboard

Supporting circular references that can be garbage collected.

Open greggman opened this issue 7 months ago • 4 comments

In JavaScript I can make two objects that reference each other and they'll still be GCed.

function makeObjectsThatReferenceEachOtherButLeakNoReferences() {
  const a = new Uint8Array(1024);
  const b = new Uint8Array(1024);
  a.other = b; // make them reference each other
  b.other = a;
}

makeObjectsThatReferenceEachOtherButLeakNoReferences();

In the code above, even though a circular reference was created, JavaScript will see there is no path from root and garbage collect the objects.

Is it possible to do the same in C++ Napi. If I make a class

class MyClass : public Napi::ObjectWrap<MyClass> {
  ...
  Napi::Reference<Napi::Object> storedObjectRef_;
};

And I manage to make 2 instanced of MyClass and set storedObjectRef_ so they point to each other, AFAICT these objects will never be garbage collected.

Is there a solution?

Note: I know I could add some function close or whatever to null out storedObjectRef_ but that's not really the question I'm asking. I'm trying to reproduce JS garbage collecting circular references.

One idea I guess, which appears to work, is I could add a JS property to MyClass. So instead of Napi::Reference<Napi::Object> storageObjectRef_ I'd use Get, Set as in

this->Value().Set("storageObjectRef", otherObject);

but unfortunately that's visible externally which I don't want. I could use a symbol but those are inspectable too. Though it might be better than nothing if there is no other solutions.

greggman avatar Jun 07 '25 00:06 greggman

I'm pretty sure napi doesn't support this but it occurs to me in JS there are private properties

 class Foo {
    #myPrivProp
 }

if there was a way to make them from a C++ class in DefineClass that would also solve the issue .

greggman avatar Jun 07 '25 15:06 greggman

It is possible to make an Napi::Reference as a weak reference:

https://github.com/nodejs/node-addon-api/blob/ff6a672b07502116a4db8fd3c7000ba6dcb62ec8/napi.h#L1768-L1771

legendecas avatar Jun 13 '25 15:06 legendecas

Unless I'm mis-understanding, a weak reference is not a solution. The JS example above, those are not weak references. Just like the JS, I need 2 objects to be able to point to each other and keep each other alive as long as they are both referenced but, like JS, if there is no path from root they get GCed.

greggman avatar Jun 13 '25 16:06 greggman

This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.

github-actions[bot] avatar Sep 12 '25 00:09 github-actions[bot]