如何清理在 Local<External> 引用中跟踪的 C++ 对象?
How can I clean up C++ objects tracked in Local<External> references?
我正在创建纯 C++ 对象(仅来自 C++),然后将它们附加到作为 API 包装器实例返回的 JS 公开对象。附件的方式是使用通过 SetInternalField
存储的 External::New
- 几乎完全遵循 V8 Embedder's Guide.
中概述的模式
这些对象本应由 JS 进行内存管理,但即使在 JS 中删除引用并触发垃圾回收后,C++ 对象仍然存在。不调用析构函数,如果我在别处保存一个独立引用,它仍然有效。
我认为 V8 的局部值清理不知道如何(或是否)破坏 void*
的内容是有道理的,但这不是一个突出的用例吗?一般而言,我无法找到以任何方式挂钩到外部或本地的 de-scoping/cleanup。此外,我对 persistents 所做的任何事情(比如 MakeWeak
)都不会影响在 JS 端变得无法访问的局部变量(对吧?)。
当包含它们的 JS 包装器对象超出范围时,我如何确保这些 C++ 对象最终(最好是立即)被销毁?
传递给 JS 的实例化示例:
Local<Value> createWindow(HWND handle, Isolate* isolate) {
// Build an instance from a premade FunctionTemplate with object
// and method prototypes, and SetInternalFieldCount(1)
Local<Function> fn = Local<Function>::New(isolate, window);
constructingInternally = true;
Local<Object> obj = fn->NewInstance(Context::New(isolate)).ToLocalChecked();
constructingInternally = false;
CppWindow* win = CppWindow(handle);
lastWin = win; // added for debugging
obj->SetInternalField(0, External::New(isolate, win));
return obj;
}
更新:经过数小时的盲目、反复试验猜测后,我终于有了可以编译的代码和在对象被垃圾回收时触发的回调。尝试在回调中使用 Local<External>;
或包含的 Local<Object>;
浪费了很多时间,我无缘无故地理解,这些尝试会拒绝编译并出现令人困惑的错误(例如“'Local<External>' 没有成员 'Value'”,或“'Local<Object>' 没有成员 'GetInternalField'”)。当他们最终编译时,在试图到达我的对象的过程中,进程会在回调中默默地死去。最后我明白了,直接传入了一个指向对象的指针,现在我可以直接删除它了。
Local<Value> createWindow(HWND handle, Isolate* isolate) {
Local<Function> fn = Local<Function>::New(isolate, window);
constructingInternally = true;
Local<Object> obj = fn->NewInstance(Context::New(isolate)).ToLocalChecked();
constructingInternally = false;
CppWindow* win = CppWindow(handle);
obj->SetInternalField(0, External::New(isolate, win));
Persistent<Object>(isolate, obj).SetWeak(
win,
[](const WeakCallbackInfo<CppWindow>& data) {
delete data.GetParameter();
},
WeakCallbackType::kParameter
);
return obj;
}
没有一个代码示例可以证明这种简单、直接的用例是完全犯罪的。 jmrk 提供的第二个 link 至少让我能够拼凑出最终解决问题所需的一些模糊线索,但这些来源几乎等量地误导了 C++ 新手。
正确,V8 无法神奇地猜测您的 C++ 对象是否存在,或者如何释放它们。
解决这个问题的通常方法是使用 Persistents。对于弱持久句柄,可以设置回调,当 V8 释放对象的最后一个引用时调用。在该回调中,您随后可以调用释放任何依赖的 C++ 对象所需的任何析构函数。
如果您可以预测对象的生命周期(例如,取决于您的应用程序在做什么,"while a request is being handled" 或类似的),一个潜在的替代方案是您自己管理对象。该选项可能更快,可能更容易实现,或者可能不可能,如果你无法预测对象的生命周期。
我正在创建纯 C++ 对象(仅来自 C++),然后将它们附加到作为 API 包装器实例返回的 JS 公开对象。附件的方式是使用通过 SetInternalField
存储的 External::New
- 几乎完全遵循 V8 Embedder's Guide.
这些对象本应由 JS 进行内存管理,但即使在 JS 中删除引用并触发垃圾回收后,C++ 对象仍然存在。不调用析构函数,如果我在别处保存一个独立引用,它仍然有效。
我认为 V8 的局部值清理不知道如何(或是否)破坏 void*
的内容是有道理的,但这不是一个突出的用例吗?一般而言,我无法找到以任何方式挂钩到外部或本地的 de-scoping/cleanup。此外,我对 persistents 所做的任何事情(比如 MakeWeak
)都不会影响在 JS 端变得无法访问的局部变量(对吧?)。
当包含它们的 JS 包装器对象超出范围时,我如何确保这些 C++ 对象最终(最好是立即)被销毁?
传递给 JS 的实例化示例:
Local<Value> createWindow(HWND handle, Isolate* isolate) {
// Build an instance from a premade FunctionTemplate with object
// and method prototypes, and SetInternalFieldCount(1)
Local<Function> fn = Local<Function>::New(isolate, window);
constructingInternally = true;
Local<Object> obj = fn->NewInstance(Context::New(isolate)).ToLocalChecked();
constructingInternally = false;
CppWindow* win = CppWindow(handle);
lastWin = win; // added for debugging
obj->SetInternalField(0, External::New(isolate, win));
return obj;
}
更新:经过数小时的盲目、反复试验猜测后,我终于有了可以编译的代码和在对象被垃圾回收时触发的回调。尝试在回调中使用 Local<External>;
或包含的 Local<Object>;
浪费了很多时间,我无缘无故地理解,这些尝试会拒绝编译并出现令人困惑的错误(例如“'Local<External>' 没有成员 'Value'”,或“'Local<Object>' 没有成员 'GetInternalField'”)。当他们最终编译时,在试图到达我的对象的过程中,进程会在回调中默默地死去。最后我明白了,直接传入了一个指向对象的指针,现在我可以直接删除它了。
Local<Value> createWindow(HWND handle, Isolate* isolate) {
Local<Function> fn = Local<Function>::New(isolate, window);
constructingInternally = true;
Local<Object> obj = fn->NewInstance(Context::New(isolate)).ToLocalChecked();
constructingInternally = false;
CppWindow* win = CppWindow(handle);
obj->SetInternalField(0, External::New(isolate, win));
Persistent<Object>(isolate, obj).SetWeak(
win,
[](const WeakCallbackInfo<CppWindow>& data) {
delete data.GetParameter();
},
WeakCallbackType::kParameter
);
return obj;
}
没有一个代码示例可以证明这种简单、直接的用例是完全犯罪的。 jmrk 提供的第二个 link 至少让我能够拼凑出最终解决问题所需的一些模糊线索,但这些来源几乎等量地误导了 C++ 新手。
正确,V8 无法神奇地猜测您的 C++ 对象是否存在,或者如何释放它们。
解决这个问题的通常方法是使用 Persistents。对于弱持久句柄,可以设置回调,当 V8 释放对象的最后一个引用时调用。在该回调中,您随后可以调用释放任何依赖的 C++ 对象所需的任何析构函数。
如果您可以预测对象的生命周期(例如,取决于您的应用程序在做什么,"while a request is being handled" 或类似的),一个潜在的替代方案是您自己管理对象。该选项可能更快,可能更容易实现,或者可能不可能,如果你无法预测对象的生命周期。