[v8] Algunas trampas sobre las fugas de memoria

1.Persistent.Reset no es una referencia

código fuente 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_);
}

Entonces, si se usa NewPersistent.Reset(OldPersistent) en el código, llame a OldPersistent.Reset() para liberarlo.

 

2. NewInstance causará un Constructor residual, la creación de uno generará dos, la liberación solo puede liberar uno.

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();

 

 

pd: use la biblioteca de volcado de pila kill -USR2 [número de proceso] para generar el archivo de volcado de pila. El tercer constructor debe corresponder al primero, que es 100, pero el resultado es 200. Y cuando el programa realiza el trabajo de limpieza, solo puede se lanzará Drop 100. Es decir, cada vez que NewInstance generará primero un dato, que está fuera de nuestro control, debería liberarse después del alcance del tipo local, pero no lo es. Después de eso, usamos GetReturnValue().Set(js); para ingresar v8 es una operación de copia profunda, por lo que se forman dos, y el lanzamiento solo puede lanzar el v8 posterior.

 

Usé la biblioteca Nan antes y descubrí que hay una brecha entre la versión anterior y la nueva.

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));
}

Entre ellos, el método Escape copia el valor del parámetro en un campo cerrado, luego elimina otros identificadores locales y finalmente devuelve una copia del nuevo identificador que se puede devolver de forma segura.

http://www.360doc.com/content/16/0701/22/832545_572287892.shtml

Bajo este modelo, hay una trampa muy común a tener en cuenta: no puede devolver directamente un identificador local desde una función que declara un alcance de identificador. Si hace esto, el identificador nativo que intenta devolver se eliminará en el destructor del alcance del identificador antes de que la función regrese. La forma correcta es usar EscapableHandleScope en lugar de HandleScope para crear un campo de identificador, luego llamar al método Escape y pasar el identificador que desea devolver.

Sospecho que es causado por esta operación, incluso si hay algunos problemas con la biblioteca Nan, así que decidí abandonar la biblioteca Nan.

Primero intente cons->NewInstance(isolate->GetCurrentContext(),3,argv).ToLocalChecked(); todavía no funciona. Me pregunto si es un problema con el nuevo método de Nan para construir FunctionTemplate.

Local<FunctionTemplate> tpg = Nan::New<FunctionTemplate>(Nuevo);

 

Sobre esta base, cambie FunctionTemplate a un método nativo

Local<FunctionTemplate> tpg = Nan::New<FunctionTemplate>(Nuevo); -->Local<FunctionTemplate> tpg = FunctionTemplate::New(isolate);

Se destruirá por adelantado, lo que dará como resultado un puntero ilegal cuando se use, y el alcance visible seguirá siendo diferente.

 

Vuelva a escribir generateJSInstance, cancele el uso de todos los métodos de la biblioteca Nan y use métodos nativos v8 para implementar

 //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 ;

 

 

El resultado sigue siendo inválido.

Entonces, la conclusión es que todavía es un problema causado por NewInstance nativo de v8.

NewInstance llamará a NewInstanceWithSideEffectType

Código fuente 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);
}

Usar el NewInstanceWithSideEffectType nativo para configurar SideEffectType::kHasNoSideEffect no tiene ningún efecto,

/**
 * 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
};

Por lo tanto, actualmente no hay solución para este problema.

 

Supongo que te gusta

Origin blog.csdn.net/sm9sun/article/details/107480438
Recomendado
Clasificación