Object lifetime management#

As Node-API calls are made, handles to objects in the heap for the underlying VM may be returned as napi_values. These handles must hold the objects 'live' until they are no longer required by the native code, otherwise the objects could be collected before the native code was finished using them.

As object handles are returned they are associated with a 'scope'. The lifespan for the default scope is tied to the lifespan of the native method call. The result is that, by default, handles remain valid and the objects associated with these handles will be held live for the lifespan of the native method call.

In many cases, however, it is necessary that the handles remain valid for either a shorter or longer lifespan than that of the native method. The sections which follow describe the Node-API functions that can be used to change the handle lifespan from the default.

Making handle lifespan shorter than that of the native method#

It is often necessary to make the lifespan of handles shorter than the lifespan of a native method. For example, consider a native method that has a loop which iterates through the elements in a large array:

for (int i = 0; i < ; i++) {
napi_value result;
napi_status status = napi_get_element(env, object, i, &result);
if (status != napi_ok) {
break;
}
// do something with element
}

This would result in a large number of handles being created, consuming substantial resources. In addition, even though the native code could only use the most recent handle, all of the associated objects would also be kept alive since they all share the same scope.

To handle this case, Node-API provides the ability to establish a new 'scope' to which newly created handles will be associated. Once those handles are no longer required, the scope can be 'closed' and any handles associated with the scope are invalidated. The methods available to open/close scopes are napi_open_handle_scope and napi_close_handle_scope.

Node-API only supports a single nested hierarchy of scopes. There is only one active scope at any time, and all new handles will be associated with that scope while it is active. Scopes must be closed in the reverse order from which they are opened. In addition, all scopes created within a native method must be closed before returning from that method.

Taking the earlier example, adding calls to napi_open_handle_scope and napi_close_handle_scope would ensure that at most a single handle is valid throughout the execution of the loop:

for (int i = 0; i < ; i++) {
napi_handle_scope scope;
napi_status status = napi_open_handle_scope(env, &scope);
if (status != napi_ok) {
break;
}
napi_value result;
status = napi_get_element(env, object, i, &result);
if (status != napi_ok) {
break;
}
// do something with element
status = napi_close_handle_scope(env, scope);
if (status != napi_ok) {
break;
}
}

When nesting scopes, there are cases where a handle from an inner scope needs to live beyond the lifespan of that scope. Node-API supports an 'escapable scope' in order to support this case. An escapable scope allows one handle to be 'promoted' so that it 'escapes' the current scope and the lifespan of the handle changes from the current scope to that of the outer scope.

The methods available to open/close escapable scopes are napi_open_escapable_handle_scope and napi_close_escapable_handle_scope.

The request to promote a handle is made through napi_escape_handle which can only be called once.

napi_open_handle_scope#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env,
napi_handle_scope* result)
;

Returns napi_ok if the API succeeded.

This API opens a new scope.

napi_close_handle_scope#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env,
napi_handle_scope scope)
;

Returns napi_ok if the API succeeded.

This API closes the scope passed in. Scopes must be closed in the reverse order from which they were created.

This API can be called even if there is a pending JavaScript exception.

napi_open_escapable_handle_scope#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status
napi_open_escapable_handle_scope(napi_env env,
napi_handle_scope* result)
;

Returns napi_ok if the API succeeded.

This API opens a new scope from which one object can be promoted to the outer scope.

napi_close_escapable_handle_scope#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status
napi_close_escapable_handle_scope(napi_env env,
napi_handle_scope scope)
;

Returns napi_ok if the API succeeded.

This API closes the scope passed in. Scopes must be closed in the reverse order from which they were created.

This API can be called even if there is a pending JavaScript exception.

napi_escape_handle#
Added in: v8.0.0 N-API version: 1
napi_status napi_escape_handle(napi_env env,
napi_escapable_handle_scope scope,
napi_value escapee,
napi_value* result)
;

Returns napi_ok if the API succeeded.

This API promotes the handle to the JavaScript object so that it is valid for the lifetime of the outer scope. It can only be called once per scope. If it is called more than once an error will be returned.

This API can be called even if there is a pending JavaScript exception.

References to objects with a lifespan longer than that of the native method#

In some cases an addon will need to be able to create and reference objects with a lifespan longer than that of a single native method invocation. For example, to create a constructor and later use that constructor in a request to creates instances, it must be possible to reference the constructor object across many different instance creation requests. This would not be possible with a normal handle returned as a napi_value as described in the earlier section. The lifespan of a normal handle is managed by scopes and all scopes must be closed before the end of a native method.

Node-API provides methods to create persistent references to an object. Each persistent reference has an associated count with a value of 0 or higher. The count determines if the reference will keep the corresponding object live. References with a count of 0 do not prevent the object from being collected and are often called 'weak' references. Any count greater than 0 will prevent the object from being collected.

References can be created with an initial reference count. The count can then be modified through napi_reference_ref and napi_reference_unref. If an object is collected while the count for a reference is 0, all subsequent calls to get the object associated with the reference napi_get_reference_value will return NULL for the returned napi_value. An attempt to call napi_reference_ref for a reference whose object has been collected results in an error.

References must be deleted once they are no longer required by the addon. When a reference is deleted, it will no longer prevent the corresponding object from being collected. Failure to delete a persistent reference results in a 'memory leak' with both the native memory for the persistent reference and the corresponding object on the heap being retained forever.

There can be multiple persistent references created which refer to the same object, each of which will either keep the object live or not based on its individual count. Multiple persistent references to the same object can result in unexpectedly keeping alive native memory. The native structures for a persistent reference must be kept alive until finalizers for the referenced object are executed. If a new persistent reference is created for the same object, the finalizers for that object will not be run and the native memory pointed by the earlier persistent reference will not be freed. This can be avoided by calling napi_delete_reference in addition to napi_reference_unref when possible.

napi_create_reference#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result)
;

Returns napi_ok if the API succeeded.

This API creates a new reference with the specified reference count to the Object passed in.

napi_delete_reference#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref);

Returns napi_ok if the API succeeded.

This API deletes the reference passed in.

This API can be called even if there is a pending JavaScript exception.

napi_reference_ref#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_reference_ref(napi_env env,
napi_ref ref,
uint32_t* result)
;

Returns napi_ok if the API succeeded.

This API increments the reference count for the reference passed in and returns the resulting reference count.

napi_reference_unref#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_reference_unref(napi_env env,
napi_ref ref,
uint32_t* result)
;

Returns napi_ok if the API succeeded.

This API decrements the reference count for the reference passed in and returns the resulting reference count.

napi_get_reference_value#
Added in: v8.0.0 N-API version: 1
NAPI_EXTERN napi_status napi_get_reference_value(napi_env env,
napi_ref ref,
napi_value* result)
;

the napi_value passed in or out of these methods is a handle to the object to which the reference is related.

Returns napi_ok if the API succeeded.

If still valid, this API returns the napi_value representing the JavaScript Object associated with the napi_ref. Otherwise, result will be NULL.

Cleanup on exit of the current Node.js instance#

While a Node.js process typically releases all its resources when exiting, embedders of Node.js, or future Worker support, may require addons to register clean-up hooks that will be run once the current Node.js instance exits.

Node-API provides functions for registering and un-registering such callbacks. When those callbacks are run, all resources that are being held by the addon should be freed up.

napi_add_env_cleanup_hook#
Added in: v10.2.0 N-API version: 3
NODE_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
void* arg)
;

Registers fun as a function to be run with the arg parameter once the current Node.js environment exits.

A function can safely be specified multiple times with different arg values. In that case, it will be called multiple times as well. Providing the same fun and arg values multiple times is not allowed and will lead the process to abort.

The hooks will be called in reverse order, i.e. the most recently added one will be called first.

Removing this hook can be done by using napi_remove_env_cleanup_hook. Typically, that happens when the resource for which this hook was added is being torn down anyway.

For asynchronous cleanup, napi_add_async_cleanup_hook is available.

napi_remove_env_cleanup_hook#
Added in: v10.2.0 N-API version: 3
NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,
void (*fun)(void* arg),
void* arg)
;

Unregisters fun as a function to be run with the arg parameter once the current Node.js environment exits. Both the argument and the function value need to be exact matches.

The function must have originally been registered with napi_add_env_cleanup_hook, otherwise the process will abort.

napi_add_async_cleanup_hook#
History
Version版本Changes变更
v14.8.0, v12.19.0

Added in: v14.8.0, v12.19.0

v14.10.0, v12.19.0

Changed signature of the hook callback.

N-API version: 8
NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
napi_env env,
napi_async_cleanup_hook hook,
void* arg,
napi_async_cleanup_hook_handle* remove_handle)
;

Registers hook, which is a function of type napi_async_cleanup_hook, as a function to be run with the remove_handle and arg parameters once the current Node.js environment exits.

Unlike napi_add_env_cleanup_hook, the hook is allowed to be asynchronous.

Otherwise, behavior generally matches that of napi_add_env_cleanup_hook.

If remove_handle is not NULL, an opaque value will be stored in it that must later be passed to napi_remove_async_cleanup_hook, regardless of whether the hook has already been invoked. Typically, that happens when the resource for which this hook was added is being torn down anyway.

napi_remove_async_cleanup_hook#
History
Version版本Changes变更
v14.8.0, v12.19.0

Added in: v14.8.0, v12.19.0

v14.10.0, v12.19.0

Removed env parameter.

NAPI_EXTERN napi_status napi_remove_async_cleanup_hook(
napi_async_cleanup_hook_handle remove_handle)
;

Unregisters the cleanup hook corresponding to remove_handle. This will prevent the hook from being executed, unless it has already started executing. This must be called on any napi_async_cleanup_hook_handle value obtained from napi_add_async_cleanup_hook.