You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Call the SuppressDestruct() method on the static data prevent the calling
// to this destructor to reset the reference when the environment is no longer
// available.
constructor.SuppressDestruct();
But what exactly means this comment? I honestly don't understand if I have to manage the life cycle of any object created by this code.
2. Similarly if using a Napi::FunctionReference created with Napi::Persistent() and assigned to a class member to call a JS callback at any point in time, should the code take care of dereferencing it at some point? https://github.com/nodejs/node-addon-api/blob/master/doc/function_reference.md
Simply put, the v8 engine works with reference counting for garbage collection. The Napi::Function func constructor function is created on the stack inside the Init method, but it stored statically in constructor. We need to tell the engine that we are still using this value even after Init finishes, which is why we store it in a reference.
Classes that extend Reference have a destructor to call napi_delete_reference on themselves unlessSupressDestruct() has been called.
One small correction. I don't think that V8 uses reference counting, but that does not change the comment that if we use the method across methods, that we need to either have a reference to it in a live JavaScript Object or tell V8 that it needs to be kept alive.
Sure I understand now, still to complete a 100% shutdown of the module, some Uninit() method should dereference all these static properties then?
It is not clear to me when a node module goes out of scope, how exactly that works out on the allocated memory, but it could be lack of understanding of how Node modules are allocated/deallocated.
@marco-ms The life cycle of Node.js native addons coincides with that of the Node.js environment. This means that once a Node.js module is loaded, it is not unloaded unless the Node.js environment terminates. For the main thread, this means that modules are unloaded only when the process terminates. For worker threads, this means that modules are unloaded when the worker thread terminates. Until recently, strong references and weak references that were not garbage collected up until environment exit were not deleted. This changes with nodejs/node#28428 having landed. Now the environment will make one final pass over all remaining references and delete them before exiting.
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.
I'm going to close this as there has not been a response since the last response quite some time ago. Please let us know if you think that was not the right thing to do.
@mhdawson@KevinEady sorry to revive this quite old thread, I am trying to wrap my head around NAPI now that I am trying to migrate my addon to NAPI (migrating from nan, the addon is node-libcurl).
As an addon author, if I call SupressDestruct on the constructor of a class I am exposing from my module, which is then saved as a static property of its C++ class, do I need to do any cleanup on it? Given the above comments, I would assume no.
Imagine I am not using env.SetInstanceData to keep track of these, so I need the static reference around in case I need to create these instances in another function.
Another follow-up question is, does that make my addon not thread-safe? If I want to stay thread-safe, am I required to use SetInstanceData?
And one last question/confirmation: I suppose that if I have multiple constructors (from multiple classes I am exposing using object_wrap), I need to have some struct to store all of them as the environment instance data, given that the following calls to that method will overwrite the previously set value. Is that correct?
if I call SupressDestruct on the constructor of a class I am exposing from my module, which is then saved as a static property of its C++ class, do I need to do any cleanup on it? Given the above comments, I would assume no.
The FunctionReference will be cleaned up on environment shutdown, and you don't need to worry about it unless you have some additional finalizer that cleans up non-Node.js data (eg. some dynamically created new data to clean with delete)
Imagine I am not using env.SetInstanceData to keep track of these, so I need the static reference around in case I need to create these instances in another function.
Another follow-up question is, does that make my addon not thread-safe? If I want to stay thread-safe, am I required to use SetInstanceData?
Yes, because if your addon is loaded in multiple environments, the static data set in Instance 1 will be overwritten by Instance 2, and when Instance 1 calls it, the references would be invalid.
And one last question/confirmation: I suppose that if I have multiple constructors (from multiple classes I am exposing using object_wrap), I need to have some struct to store all of them as the environment instance data, given that the following calls to that method will overwrite the previously set value. Is that correct?
One solution: Your addon can only have one entry point , eg.Napi::Value Init( Napi::Env env, Napi:: Object exports ). If you pass these arguments to your individual objectwrap constructors eg. MyObject1::Init( env, exports ); and MyObject2::Init( env, exports ), and these functions modify exports by adding the class declaration eg. exports.set("MyObject1", DefineClass(...) ); and exports.set("MyObject2", DefineClass(...) ); , then your addon's main Init can SetInstanceData to an ObjectReference of exports, and when you want to call a specific constructor within your c++ addon, you'll GetInstanceData<ObjectReference>() and can grab the specific property ("MyObject1" or "MyObject2") from the instance data.
Let me know if you have any other questions. Good luck!
Activity
KevinEady commentedon Oct 2, 2019
Simply put, the v8 engine works with reference counting for garbage collection. The
Napi::Function funcconstructor function is created on the stack inside theInitmethod, but it stored statically inconstructor. We need to tell the engine that we are still using this value even afterInitfinishes, which is why we store it in a reference.Classes that extend
Referencehave a destructor to callnapi_delete_referenceon themselves unlessSupressDestruct()has been called.mhdawson commentedon Oct 3, 2019
One small correction. I don't think that V8 uses reference counting, but that does not change the comment that if we use the method across methods, that we need to either have a reference to it in a live JavaScript Object or tell V8 that it needs to be kept alive.
marco-ms commentedon Oct 14, 2019
Sure I understand now, still to complete a 100% shutdown of the module, some Uninit() method should dereference all these static properties then?
It is not clear to me when a node module goes out of scope, how exactly that works out on the allocated memory, but it could be lack of understanding of how Node modules are allocated/deallocated.
gabrielschulhof commentedon Oct 15, 2019
@marco-ms The life cycle of Node.js native addons coincides with that of the Node.js environment. This means that once a Node.js module is loaded, it is not unloaded unless the Node.js environment terminates. For the main thread, this means that modules are unloaded only when the process terminates. For worker threads, this means that modules are unloaded when the worker thread terminates. Until recently, strong references and weak references that were not garbage collected up until environment exit were not deleted. This changes with nodejs/node#28428 having landed. Now the environment will make one final pass over all remaining references and delete them before exiting.
github-actions commentedon Dec 17, 2020
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.
mhdawson commentedon Dec 17, 2020
I'm going to close this as there has not been a response since the last response quite some time ago. Please let us know if you think that was not the right thing to do.
JCMais commentedon Sep 2, 2025
@mhdawson @KevinEady sorry to revive this quite old thread, I am trying to wrap my head around NAPI now that I am trying to migrate my addon to NAPI (migrating from nan, the addon is node-libcurl).
As an addon author, if I call SupressDestruct on the constructor of a class I am exposing from my module, which is then saved as a static property of its C++ class, do I need to do any cleanup on it? Given the above comments, I would assume no.
Imagine I am not using env.SetInstanceData to keep track of these, so I need the static reference around in case I need to create these instances in another function.
Another follow-up question is, does that make my addon not thread-safe? If I want to stay thread-safe, am I required to use SetInstanceData?
And one last question/confirmation: I suppose that if I have multiple constructors (from multiple classes I am exposing using object_wrap), I need to have some struct to store all of them as the environment instance data, given that the following calls to that method will overwrite the previously set value. Is that correct?
KevinEady commentedon Sep 4, 2025
Hi @JCMais ,
The
FunctionReferencewill be cleaned up on environment shutdown, and you don't need to worry about it unless you have some additional finalizer that cleans up non-Node.js data (eg. some dynamically creatednewdata to clean withdelete)Yes, because if your addon is loaded in multiple environments, the static data set in Instance 1 will be overwritten by Instance 2, and when Instance 1 calls it, the references would be invalid.
One solution: Your addon can only have one entry point , eg.
Napi::Value Init( Napi::Env env, Napi:: Object exports ). If you pass these arguments to your individual objectwrap constructors eg.MyObject1::Init( env, exports );andMyObject2::Init( env, exports ), and these functions modifyexportsby adding the class declaration eg.exports.set("MyObject1", DefineClass(...) );andexports.set("MyObject2", DefineClass(...) );, then your addon's mainInitcanSetInstanceDatato anObjectReferenceofexports, and when you want to call a specific constructor within your c++ addon, you'llGetInstanceData<ObjectReference>()and can grab the specific property ("MyObject1"or"MyObject2") from the instance data.Let me know if you have any other questions. Good luck!