c++ - 如何清理在 Local<External> 引用中跟踪的 C++ 对象?

标签 c++ node.js garbage-collection v8

我正在创建纯 C++ 对象(仅来自 C++),然后将它们附加到作为 API 包装器实例返回的 JS 公开对象。附件的方式是使用External::New通过 SetInternalField 存储- 几乎完全遵循 V8 Embedder's Guide 中概述的模式.

这些对象本应由 JS 进行内存管理,但即使在 JS 中删除引用并触发垃圾回收后,C++ 对象仍然存在。不调用析构函数,如果我在别处保存独立引用,它仍然有效。

我认为 V8 的局部值清理不知道如何(或是否)破坏 void* 的内容是有道理的,但这不是一个突出的用例吗?一般而言,我无法找到以任何方式 Hook 外部或本地的范围界定/清理。此外,我对持久性变量(如 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'没有成员'Value'”,或“'Local'没有成员'获取内部字段'")。当他们最终编译时,在试图到达我的对象的过程中,进程会在回调中默默地死去。最终我想通了,直接传递了一个指向该对象的指针,现在我可以直接删除它了。

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 提供的第二个链接至少让我能够拼凑出最终解决问题所需的一些模糊线索,但这些来源几乎等量地误导了 C++ 新手。

最佳答案

正确,V8 无法神奇地猜测您的 C++ 对象是否存在,或者如何释放它们。

解决这个问题的通常方法是使用 Persistents。对于弱持久句柄,可以设置回调,当 V8 释放对象的最后一个引用时调用。在该回调中,您随后可以调用释放任何依赖的 C++ 对象所需的任何析构函数。

一个潜在的替代方案是自己管理对象,如果您可以预测它们的生命周期(例如,取决于您的应用程序正在做什么,“在处理请求时”或类似的)。该选项可能更快,可能更易于实现,或者如果您无法预测对象生命周期,可能不可能。

关于c++ - 如何清理在 Local<External> 引用中跟踪的 C++ 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43550539/