1.Persistent.Reset并非引用
v8源码:
V8_INLINE Persistent() : PersistentBase<T>(nullptr) {}
template <class T>
void PersistentBase<T>::Reset() {
if (this->IsEmpty()) return;
V8::DisposeGlobal(reinterpret_cast<internal::Address*>(this->val_));
val_ = nullptr;
}
template <class T>
template <class S>
void PersistentBase<T>::Reset(Isolate* isolate, const Local<S>& other) {
static_assert(std::is_base_of<T, S>::value, "type check");
Reset();
if (other.IsEmpty()) return;
this->val_ = New(isolate, other.val_);
}
template <class T>
template <class S>
void PersistentBase<T>::Reset(Isolate* isolate,
const PersistentBase<S>& other) {
static_assert(std::is_base_of<T, S>::value, "type check");
Reset();
if (other.IsEmpty()) return;
this->val_ = New(isolate, other.val_);
}
所以如果代码中使用了NewPersistent.Reset(OldPersistent),要调用OldPersistent.Reset()释放掉。
2.NewInstance会导致残留Constructor,创建一个会生成两个,释放只能释放一个。
Nan::HandleScope scope;
// Prepare constructor template
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
tpl->SetClassName(Nan::New(ClassName).ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(...);
Local<Function> 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<v8::Object> NewInstance(
v8::Local<v8::Function> h
, int argc
, v8::Local<v8::Value> argv[]) {
v8::Isolate *isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
return scope.Escape(h->NewInstance(isolate->GetCurrentContext(), argc, argv)
.FromMaybe(v8::Local<v8::Object>()));
}
nan_maybe_pre_43_inl.h:
inline
MaybeLocal<v8::Object> NewInstance(
v8::Local<v8::Function> h
, int argc
, v8::Local<v8::Value> argv[]) {
return MaybeLocal<v8::Object>(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> tpg = Nan::New<FunctionTemplate>(New);
在此基础上,把FunctionTemplate改成原生方法
Local<FunctionTemplate> tpg = Nan::New<FunctionTemplate>(New); -->Local<FunctionTemplate> tpg = FunctionTemplate::New(isolate);
会提前销毁,导致使用时非法指针,可见作用域还是不一样。
重写了generateJSInstance,取消Nan库所有方法的使用,都用v8原生方法实现
//Nan::HandleScope scope;
Isolate *isolate = Isolate::GetCurrent();
v8::EscapableHandleScope Escope(isolate);
HandleScope scope(isolate);
Local<FunctionTemplate> tpg = FunctionTemplate::New(isolate);
Local<String> v8name=String::NewFromUtf8(isolate, ClassName.c_str(), NewStringType::kInternalized).ToLocalChecked();
tpg->SetClassName(v8name);
tpg->InstanceTemplate()->SetInternalFieldCount(1);
Local<Function> cons = tpg->GetFunction();
Local<v8::Value> argv[3] = {..., ..., ...};
Local<Object> js;
//js = cons->NewInstance(isolate->GetCurrentContext()).ToLocalChecked();
//Escope.Escape没有效果
//js = Escope.Escape(cons->NewInstance(isolate->GetCurrentContext(),3,argv).FromMaybe(v8::Local<v8::Object>()));
js = cons->NewInstance(isolate->GetCurrentContext(),3,argv).ToLocalChecked();
//置成SideEffectType::kHasNoSideEffect没有效果
//js = cons->NewInstanceWithSideEffectType(isolate->GetCurrentContext(),3,argv,SideEffectType::kHasNoSideEffect).ToLocalChecked();
args.GetReturnValue().Set(js);
constructor.Reset();
return ;
结果依然无效。
所以结论是还是v8原生NewInstance引发的问题 。
NewInstance会调用NewInstanceWithSideEffectType
NewInstanceWithSideEffectType源码
MaybeLocal<Object> Function::NewInstanceWithSideEffectType(
Local<Context> context, int argc, v8::Local<v8::Value> argv[],
SideEffectType side_effect_type) const {
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.Execute");
ENTER_V8(isolate, context, Function, NewInstance, MaybeLocal<Object>(),
InternalEscapableScope);
i::TimerEventScope<i::TimerEventExecute> timer_scope(isolate);
auto self = Utils::OpenHandle(this);
STATIC_ASSERT(sizeof(v8::Local<v8::Value>) == sizeof(i::Handle<i::Object>));
bool should_set_has_no_side_effect =
side_effect_type == SideEffectType::kHasNoSideEffect &&
isolate->debug_execution_mode() == i::DebugInfo::kSideEffects;
if (should_set_has_no_side_effect) {
CHECK(self->IsJSFunction() &&
i::JSFunction::cast(*self).shared().IsApiFunction());
i::Object obj =
i::JSFunction::cast(*self).shared().get_api_func_data().call_code();
if (obj.IsCallHandlerInfo()) {
i::CallHandlerInfo handler_info = i::CallHandlerInfo::cast(obj);
if (!handler_info.IsSideEffectFreeCallHandlerInfo()) {
handler_info.SetNextCallHasNoSideEffect();
}
}
}
i::Handle<i::Object>* args = reinterpret_cast<i::Handle<i::Object>*>(argv);
Local<Object> result;
has_pending_exception = !ToLocal<Object>(
i::Execution::New(isolate, self, self, argc, args), &result);
if (should_set_has_no_side_effect) {
i::Object obj =
i::JSFunction::cast(*self).shared().get_api_func_data().call_code();
if (obj.IsCallHandlerInfo()) {
i::CallHandlerInfo handler_info = i::CallHandlerInfo::cast(obj);
if (has_pending_exception) {
// Restore the map if an exception prevented restoration.
handler_info.NextCallHasNoSideEffect();
} else {
DCHECK(handler_info.IsSideEffectCallHandlerInfo() ||
handler_info.IsSideEffectFreeCallHandlerInfo());
}
}
}
RETURN_ON_FAILED_EXECUTION(Object);
RETURN_ESCAPED(result);
}
使用原生的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
};