v8源码:
V8_INLINE Persistent() : PersistentBase(nullptr) {}
template
void PersistentBase::Reset() {
if (this->IsEmpty()) return;
V8::DisposeGlobal(reinterpret_cast(this->val_));
val_ = nullptr;
}
template
template
void PersistentBase::Reset(Isolate* isolate, const Local& other) {
static_assert(std::is_base_of::value, "type check");
Reset();
if (other.IsEmpty()) return;
this->val_ = New(isolate, other.val_);
}
template
template
void PersistentBase::Reset(Isolate* isolate,
const PersistentBase& other) {
static_assert(std::is_base_of::value, "type check");
Reset();
if (other.IsEmpty()) return;
this->val_ = New(isolate, other.val_);
}
所以如果代码中使用了NewPersistent.Reset(OldPersistent),要调用OldPersistent.Reset()释放掉。
Nan::HandleScope scope;
// Prepare constructor template
Local tpl = Nan::New(New);
tpl->SetClassName(Nan::New(ClassName).ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(...);
Local cons = tpl->GetFunction();
return Nan::NewInstance(cons).ToLocalChecked();
ps:使用heapdump库 kill -USR2 [进程号]产生的heapdump文件,第三个constructor本应与第一个对应,是100个,结果却是200个,且程序中执行清理工作时,只能释放掉100个。也就是每次NewInstance会先生成一份数据,内个我们不可控,本应作为Local类型的在作用域结束后应该释放,但并没有。之后我们通过GetReturnValue().Set(js);打进v8里是深拷贝的操作,于是形成了两个,释放也只能释放掉后面v8里的。
之前是使用了Nan库,发现新旧版本有差距
nan_maybe_43_inl.h:
inline
MaybeLocal NewInstance(
v8::Local h
, int argc
, v8::Local argv[]) {
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
return scope.Escape(h->NewInstance(isolate->GetCurrentContext(), argc, argv)
.FromMaybe(v8::Local()));
}
nan_maybe_pre_43_inl.h:
inline
MaybeLocal NewInstance(
v8::Local h
, int argc
, v8::Local argv[]) {
return MaybeLocal(h->NewInstance(argc, argv));
}
其中Escape 方法复制参数中的值至一个封闭的域中,然后删除其他本地句柄,最后返回这个可以被安全返回的新句柄副本。
http://www.360doc.com/content/16/0701/22/832545_572287892.shtml
在这个模型下,有一个非常常见的陷阱需要注意:你不可以直接地在一个声明了句柄域的函数中返回一个本地句柄。如果你这么做了,那么你试图返回的本地句柄,将会在函数返回之前,在句柄域的析构函数中被删除。正确的做法是使用 EscapableHandleScope 来代替 HandleScope 创建句柄域,然后调用 Escape 方法,并且传入你想要返回的句柄。
怀疑是这个操作导致的,也包括Nan库是否就是有一些问题,于是决定废弃Nan库
先尝试 cons->NewInstance(isolate->GetCurrentContext(),3,argv).ToLocalChecked();依然无效,怀疑是不是Nan的new方法构造构造FunctionTemplate出的问题。
Local
在此基础上,把FunctionTemplate改成原生方法
Local
会提前销毁,导致使用时非法指针,可见作用域还是不一样。
重写了generateJSInstance,取消Nan库所有方法的使用,都用v8原生方法实现
//Nan::HandleScope scope;
Isolate *isolate = Isolate::GetCurrent();
v8::EscapableHandleScope Escope(isolate);
HandleScope scope(isolate);
Local tpg = FunctionTemplate::New(isolate);
Local v8name=String::NewFromUtf8(isolate, ClassName.c_str(), NewStringType::kInternalized).ToLocalChecked();
tpg->SetClassName(v8name);
tpg->InstanceTemplate()->SetInternalFieldCount(1);
Local cons = tpg->GetFunction();
Local argv[3] = {..., ..., ...};
Local
结果依然无效。
所以结论是还是v8原生NewInstance引发的问题 。
NewInstance会调用NewInstanceWithSideEffectType
NewInstanceWithSideEffectType源码
MaybeLocal
使用原生的NewInstanceWithSideEffectType置成SideEffectType::kHasNoSideEffect也没有效果,
/**
* Options for marking whether callbacks may trigger JS-observable side effects.
* Side-effect-free callbacks are allowlisted during debug evaluation with
* throwOnSideEffect. It applies when calling a Function, FunctionTemplate,
* or an Accessor callback. For Interceptors, please see
* PropertyHandlerFlags's kHasNoSideEffect.
* Callbacks that only cause side effects to the receiver are allowlisted if
* invoked on receiver objects that are created within the same debug-evaluate
* call, as these objects are temporary and the side effect does not escape.
*/
enum class SideEffectType {
kHasSideEffect,
kHasNoSideEffect,
kHasSideEffectToReceiver
};