问题
I have difficulties to understand how to correctly use HandleScope and EscapableHandleScope. For example, from this Node example:
MyObject::MyObject(const Napi::CallbackInfo& info) : Napi::ObjectWrap<MyObject>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
this->val_ = info[0].As<Napi::Number>().DoubleValue();
};
Why do we need to create a new HandleScope in this case? And from this other example:
Napi::Object CreateObject(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::Object obj = Napi::Object::New(env);
obj.Set(Napi::String::New(env, "msg"), info[0].ToString());
return obj;
}
Why is it not needed here?
Also, I didn't find any example using EscapableHandleScope, when is this needed?
回答1:
For an explanation of what HandleScopes are and what to use them for, see V8's documentation, e.g. for the class Local
:
There are two types of handles: local and persistent handles.
Local handles are light-weight and transient and typically used in local operations. They are managed by HandleScopes. That means that a HandleScope must exist on the stack when they are created and that they are only valid inside of the HandleScope active during their creation. For passing a local handle to an outer HandleScope, an EscapableHandleScope and its Escape() method must be used.
And for the class HandleScope
:
A stack-allocated class that governs a number of local handles. After a handle scope has been created, all local handles will be allocated within that handle scope until either the handle scope is deleted or another handle scope is created. If there is already a handle scope and a new one is created, all allocations will take place in the new handle scope until it is deleted. After that, new handles will again be allocated in the original handle scope.
After the handle scope of a local handle has been deleted the garbage collector will no longer track the object stored in the handle and may deallocate it. The behavior of accessing a handle for which the handle scope has been deleted is undefined.
Pragmatically:
- When calling from JavaScript into C++, you'll need at least one
HandleScope
if the C++ code creates anyLocal<>
s. Usually exactly oneHandleScope
is the right number. - There's a cost to creating and destroying HandleScopes, so if you have many fine-grained HandleScopes, you're wasting time. On the other hand, a HandleScope (by design, that's its purpose!) keeps all objects alive (in the GC sense) that the handles contained in it are referring to, so for very long-running code, or loops with many iterations, you may want to introduce short-lived HandleScopes so that temporary objects you're done with can be freed.
- As the documentation says, you need an
EscapableHandleScope
if you want to return an object beyond the end of the lifetime of the scope.
回答2:
The following seems to hold true for nan, N-API, and node-addon-api:
[Handle Scope] is an abstraction used to control and modify the lifetime of objects created within a particular scope. In general, N-API values are created within the context of a handle scope. When a native method is called from JavaScript, a default handle scope will exist. If the user does not explicitly create a new handle scope, N-API values will be created in the default handle scope. For any invocations of code outside the execution of a native method (for instance, during a libuv callback invocation), the module is required to create a scope before invoking any functions that can result in the creation of JavaScript values.
Source: https://nodejs.org/api/n-api.html#n_api_napi_handle_scope
Which means in the examples given, since both expect const Napi::CallbackInfo& info
it's obvious both are called from JavaScript directly, so they already have a scope provided by the JS runtime -- the additional calls to create scope are only necessary if you want to perform memory management yourself, or in cases where your code is executing independently of the JS engine (such as on a timer, a callback from something other than JS code, etc.)
回答3:
Why do we need to create a new HandleScope in the snippet?
Actually, we can choose to not create a new HandleScope here. There is an outer scope in Node.js that is enclosing our handles created in this function. But then all the handles created in this function will live longer than necessary and handles cost resources.
When do we need EscapableHandleScope?
When a handle from an inner scope needs to live beyond the lifespan of that scope. For example, when returning data newly created in a function.
Reference
V8 embed: https://v8.dev/docs/embed#handles-and-garbage-collection
node-addon-api: https://github.com/nodejs/node-addon-api/blob/master/doc/object_lifetime_management.md
来源:https://stackoverflow.com/questions/54187588/understanding-node-addon-api-n-api-handlescope