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

时间:2017-04-21 19:26:01

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

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

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

我认为V8对本地值的清理无法知道如何(或是否)破坏void*的内容是有道理的,但这并不是一个突出的问题。用例?我无法找到任何方式对一般的外部人员或当地人的解除范围/清理工作。此外,我对持久性(如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>;的时间丢失了很多时间,我无理由理解,这些尝试会拒绝编译时出现混乱的错误(例如&#34;& #39;本地&lt;外部&gt;&#39;没有会员&#39;价值&#39;&#34;或&#34;&#39;本地&lt;对象&gt;&#39;没有会员&#39; ; GetInternalField&#39;&#34)。当他们最终编译时,在尝试到达我的对象的过程中,该进程将在回调中默默地死亡。最终我把它弄清楚并直接指向了对象,我现在可以直接删除它。

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 ++新手的部分。

1 个答案:

答案 0 :(得分:0)

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

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

一种可能的替代方法是自己管理对象,如果你可以预测它们的生命周期(例如,取决于你的应用程序正在做什么,“在处理请求时”或某些事情)。该选项可以更快,可以更容易实现,或者如果你无法预测对象的生命周期,可能是不可能的。< / p>