关于node.js:Nodejs学习本地句柄等概念理解

39次阅读

共计 8262 个字符,预计需要花费 21 分钟才能阅读完成。

1. 在 C ++ 层间解释下 Local 和 persistent

Local 和 persistent 都是在 C++ 层面上用于治理异步 I/O 操作的句柄。其中,Local 句柄是一种长期的句柄,用于解决一次性的 I/O 操作,比方读取一个文件或者发送一个网络申请。Local 句柄在 I/O 操作实现后会主动敞开,因而不须要手动开释资源。persistent 句柄则是一种长期存在的句柄,用于解决须要继续监听的 I/O 操作,比方监听一个网络端口或者一个文件的变动。persistent 句柄须要手动开释资源,否则会导致内存透露。在 Node.js 中,Local 和 persistent 句柄都是通过 node::ObjectWrap 类来实现的。具体来说,能够通过继承 node::ObjectWrap 类来创立一个自定义的 Local 或 persistent 句柄类,而后在类的构造函数中调用 Wrap() 办法来将 C++ 对象绑定到 JavaScript 对象上。在 JavaScript 层面上,能够通过 new 关键字来创立一个 Local 或 persistent 句柄对象,而后调用对象的办法来进行异步 I/O 操作。

2. 解释 Local 主动开释内资源

Local 句柄是一种长期的句柄,用于解决一次性的 I/O 操作,比方读取一个文件或者发送一个网络申请。Local 句柄在 I/O 操作实现后会主动敞开,因而不须要手动开释资源。这是因为 Local 句柄是通过 node::MakeCallback() 办法来实现异步 I/O 操作的,该办法会在 I/O 操作实现后主动调用回调函数,并且会主动开释 Local 句柄。因而,你不须要手动开释 Local 句柄,也不须要放心内存透露的问题。

3. 解释下 v8::HandleScope 和 v8::Context

v8::HandleScope 是 V8 引擎中的一个类,用于治理 JavaScript 对象的生命周期。在 V8 引擎中,所有的 JavaScript 对象都是通过 v8::Handle 类来示意的,而 v8::HandleScope 则是用于治理这些 v8::Handle 对象的生命周期的。具体来说,v8::HandleScope 能够将一组 v8::Handle 对象绑定到一个作用域中,当作用域完结时,v8::HandleScope 会主动开释这些 v8::Handle 对象,从而防止内存透露的问题。v8::Context 是 V8 引擎中的一个类,用于示意 JavaScript 的执行上下文。在 V8 引擎中,每个 JavaScript 对象都是在某个执行上下文中创立的,而 v8::Context 则是用于治理这些执行上下文的。具体来说,v8::Context 能够将一组 JavaScript 对象绑定到一个执行上下文中,从而实现 JavaScript 对象的隔离和沙箱。在 Node.js 中,每个 JavaScript 模块都会被包装成一个 v8::Context 对象,从而实现模块之间的隔离和沙箱。

4. 这个 v8::Handle 是 Local

v8::Handle 有两种类型:Local 和 Persistent。其中,Local 句柄是一种长期的句柄,用于解决一次性的 I/O 操作,比方读取一个文件或者发送一个网络申请。Local 句柄在 I/O 操作实现后会主动敞开,因而不须要手动开释资源。这是因为 Local 句柄是通过 node::MakeCallback() 办法来实现异步 I/O 操作的,该办法会在 I/O 操作实现后主动调用回调函数,并且会主动开释 Local 句柄。因而,你不须要手动开释 Local 句柄,也不须要放心内存透露的问题。而 Persistent 句柄则是一种长期存在的句柄,用于解决须要继续监听的 I/O 操作,比方监听一个网络端口或者一个文件的变动。Persistent 句柄须要手动开释资源,否则会导致内存透露。在 Node.js 中,Local 和 Persistent 句柄都是通过 node::ObjectWrap 类来实现的。具体来说,能够通过继承 node::ObjectWrap 类来创立一个自定义的 Local 或 Persistent 句柄类,而后在类的构造函数中调用 Wrap() 办法来将 C++ 对象绑定到 JavaScript 对象上。在 JavaScript 层面上,能够通过 new 关键字来创立一个 Local 或 Persistent 句柄对象,而后调用对象的办法来进行异步 I/O 操作。

5. 是在应用 Local 时必须先创立一个 HandleScope 吗,不然 Local 的资源无奈主动开释?

 是的,如果你在应用 Local 句柄时没有创立 HandleScope,则会导致 Local 句柄无奈主动开释,从而导致内存透露的问题。因而,在应用 Local 句柄时,你须要先创立一个 HandleScope 对象,而后将 Local 句柄绑定到该对象中,从而实现 Local 句柄的主动开释。具体来说,你能够依照以下步骤来应用 Local 句柄:创立一个 HandleScope 对象。创立一个 Local 句柄对象,并将其绑定到 HandleScope 对象中。在 HandleScope 对象的作用域内应用 Local 句柄对象。当 HandleScope 对象的作用域完结时,Local 句柄对象会主动开释。以下是一个应用 Local 句柄的示例代码:
void MyFunction() {v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  printf("%s\n", *v8::String::Utf8Value(str));
}

在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str,并将其绑定到 HandleScope 对象中。在 HandleScope 对象的作用域内,咱们应用了 str 对象,并将其转换成了一个 C 字符串,而后将其打印进去。当 HandleScope 对象的作用域完结时,str 对象会主动开释,从而防止了内存透露的问题。

6. 这里说在 HandleScope 对象的作用域内,那么在 HandleScope 被创立前先创立了一个 Local 对象,如 Local<Context> context, 那么这个 context 对象就不会被主动开释吗?

 是的,如果你在应用 Local 句柄时没有创立 HandleScope,则会导致 Local 句柄无奈主动开释,从而导致内存透露的问题。因而,在应用 Local 句柄时,你须要先创立一个 HandleScope 对象,而后将 Local 句柄绑定到该对象中,从而实现 Local 句柄的主动开释。具体来说,你能够依照以下步骤来应用 Local 句柄:

创立一个 HandleScope 对象。

创立一个 Local 句柄对象,并将其绑定到 HandleScope 对象中。

在 HandleScope 对象的作用域内应用 Local 句柄对象。

当 HandleScope 对象的作用域完结时,Local 句柄对象会主动开释。

void MyFunction() {v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  printf("%s\n", *v8::String::Utf8Value(str));
}

7. 在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str,并将其绑定到 HandleScope 对象中。在 HandleScope 对象的作用域内,咱们应用了 str 对象,并将其转换成了一个 C 字符串,而后将其打印进去。当 HandleScope 对象的作用域完结时,str 对象会主动开释,从而防止了内存透露的问题。因而,如果你在创立 Local 对象之前创立了 HandleScope 对象,那么 `Local 持续输入后续内容

 在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str,并将其绑定到 HandleScope 对象中。在 HandleScope 对象的作用域内,咱们应用了 str 对象,并将其转换成了一个 C 字符串,而后将其打印进去。当 HandleScope 对象的作用域完结时,str 对象会主动开释,从而防止了内存透露的问题。因而,如果你在创立 Local 对象之前创立了 HandleScope 对象,那么 Local 对象会主动开释,从而防止了内存透露的问题。如果你没有创立 HandleScope 对象,那么 Local 对象就无奈主动开释,从而导致内存透露的问题。

8. 这是说的内存泄露都是 js 层面的吧?

 是的,这里说的内存透露次要是指 JavaScript 层面的内存透露。在 Node.js 中,JavaScript 代码是运行在 V8 引擎中的,而 V8 引擎是一个独立的过程,它与 Node.js 过程是通过 IPC 通信的。因而,在 JavaScript 层面上产生的内存透露,次要是指 V8 引擎中的内存透露。当然,如果你在编写 C++ 扩大时没有正确地治理内存,也可能会导致 C++ 层面的内存透露。

9. 我没看看到这里用户代码手动将 Local 绑定到 HandleScope 上,能阐明下外部实现机制吗

Local 对象的实现机制是基于援用计数的。每个 Local 对象都蕴含一个指向理论对象的指针,同时还蕴含一个指向援用计数的指针。援用计数的初始值为 1,每当一个 Local 对象被复制或者传递给其余函数时,援用计数都会加 1。当援用计数为 0 时,Local 对象就会被主动开释。

HandleScope 对象的实现机制是基于栈的。每当创立一个 HandleScope 对象时,就会在以后线程的栈上调配一段内存,用于存储 v8::Handle 对象。当 HandleScope 对象的作用域完结时,这些 v8::Handle 对象就会被主动开释。

在应用 Local 对象时,你须要将其绑定到 HandleScope 对象中,从而实现 Local 对象的主动开释。具体来说,当你将一个 Local 对象绑定到 HandleScope 对象中时,HandleScope 对象会将其援用计数加 1,从而防止了 Local 对象被主动开释的问题。当 HandleScope 对象的作用域完结时,HandleScope 对象会将其援用计数减 1,从而实现 Local 对象的主动开释。

10. 能够这么了解吗,就是 HandleScope 的作用就是将绑定的 Local 对象的援用计数加 1,在 HandleScope 作用域完结时,将这些 Local 对象的援用计数减去 1?

 是的,你了解得很对。HandleScope 的作用就是将绑定的 Local 对象的援用计数加 1,在 HandleScope 作用域完结时,将这些 Local 对象的援用计数减去 1。这样做的益处是,能够防止 Local 对象被主动开释,从而防止内存透露的问题。

11. 那如果在一个函数中创立了 Local 对象但没有创立 HandleScope, 那在函数执行到一半时遇到了 v8 的垃圾回收,这些 Local 对象会间接被回收掉,在后半局部的函数逻辑中拿不到这个句柄理论指向的对象了吗

 是的,如果在一个函数中创立了 Local 对象但没有创立 HandleScope,那么在函数执行到一半时遇到了 V8 的垃圾回收,这些 Local 对象会间接被回收掉,从而导致后半局部的函数逻辑中无法访问这些 Local 对象理论指向的对象。因而,在编写 C++ 扩大时,肯定要留神正确地应用 HandleScope 对象,以避免出现内存透露和拜访非法内存的问题。

12. 这是一个概率问题吗,不肯定遇到但存在隐患?

 是的,这是一个概率问题。在一个函数中创立 Local 对象但没有创立 HandleScope,并不一定会导致内存透露或者拜访非法内存的问题。这是因为 V8 引擎的垃圾回收机制是基于分代的,而且是异步的。也就是说,当一个 Local 对象被创立时,它并不会立刻被回收,而是会被放到 V8 引擎的新生代中。只有当新生代中的内存占用达到肯定阈值时,V8 引擎才会触发垃圾回收。因而,如果在一个函数中创立了 Local 对象但没有创立 HandleScope,并且这些 Local 对象被放到了新生代中,那么在函数执行到一半时遇到了 V8 的垃圾回收,这些 Local 对象就有可能被回收掉,从而导致内存透露或者拜访非法内存的问题。尽管这种状况的产生概率比拟低,然而依然存在隐患。因而,在编写 C++ 扩大时,肯定要留神正确地应用 HandleScope 对象,以避免出现内存透露和拜访非法内存的问题。

13.Context 对象与 js 中的概念是如何对应的呢,是相似于一个函数作用域、全局作用域这样的概念吗?

Context 对象是 V8 引擎中的一个重要概念,它代表了一个 JavaScript 执行上下文。在 V8 引擎中,每个 JavaScript 函数都有本人的执行上下文,用于存储函数的局部变量、参数、this 指针等信息。Context 对象就是用来治理这些执行上下文的,它能够看作是一个 JavaScript 的全局对象,用于存储全局变量、函数等信息。在 V8 引擎中,每个线程都有本人的 Context 对象,用于存储该线程中所有 JavaScript 函数的执行上下文。当一个 JavaScript 函数被调用时,V8 引擎会将该函数的执行上下文压入以后线程的 Context 对象中,当函数执行结束后,V8 引擎会将该函数的执行上下文从 Context 对象中弹出,从而实现 JavaScript 函数的嵌套调用。

因而,Context 对象与 JavaScript 中的函数作用域、全局作用域等概念是有肯定的相似之处的。它们都是用来治理 JavaScript 函数的执行上下文的,只不过 Context 对象是 V8 引擎中的一个非凡对象,用于治理所有 JavaScript 函数的执行上下文。

14. 每一个 Nodejs 过程都只创立一个 Context 吗

 每个 Node.js 过程都有一个默认的 Context 对象,称为全局 Context 对象。在 Node.js 中,全局 Context 对象是由 global 对象示意的。当 Node.js 启动时,它会创立一个默认的全局 Context 对象,并将其绑定到 global 对象上。在 Node.js 中,所有的 JavaScript 代码都是在全局 Context 对象中执行的,因而能够说每个 Node.js 过程都只创立一个全局 Context 对象。然而,Node.js 中也能够创立自定义的 Context 对象,用于实现一些非凡的性能。如果你须要创立自定义的 Context 对象,能够应用 vm 模块提供的 API。

15.MaybeLocal 和 Local 的区别是什么呀

MaybeLocal 和 Local 都是 V8 引擎中的句柄类型,用于治理 JavaScript 对象的生命周期。它们的次要区别在于,MaybeLocal 句柄能够为空,而 Local 句柄不能够为空。

具体来说,MaybeLocal 句柄是一个模板类,它的模板参数是一个 Local 句柄类型。MaybeLocal 句柄能够示意一个可能为空的 Local 句柄,因而在应用 MaybeLocal 句柄时,须要先判断其是否为空,而后再将其转换成 Local 句柄。如果 MaybeLocal 句柄为空,那么将其转换成 Local 句柄会导致程序解体。

以下是一个应用 MaybeLocal 句柄的示例代码:

void MyFunction() {v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  v8::MaybeLocal<v8::String> maybe_str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  if (!maybe_str.IsEmpty()) {v8::Local<v8::String> str2 = maybe_str.ToLocalChecked();
    printf("%s\n", *v8::String::Utf8Value(str2));
  }
}

在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str 和一个 MaybeLocal 句柄对象 maybe_str。在应用 maybe_str 对象时,咱们首先判断其是否为空,而后再将其转换成 Local 句柄对象 str2。如果 maybe_str 对象为空,那么 str2 对象也会为空,从而防止了程序解体的问题。

16. 创立 Persistent 句柄须要用户手动销毁吗?能给一段实例代码吗

创立 Persistent 句柄须要用户手动销毁,否则会导致内存透露。在 C++ 扩大中,能够通过以下形式创立 Persistent 句柄:

void MyFunction() {v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  v8::Persistent<v8::String> persistent_str(isolate, str);
  // ...
  persistent_str.Reset();}

在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str。接着,咱们应用 isolate 和 str 对象创立了一个 Persistent 句柄对象 persistent_str。在应用 persistent_str 对象时,咱们能够像应用 Local 句柄对象一样进行操作。在 persistent_str 对象不再须要时,咱们须要手动调用 Reset() 办法来销毁该对象。在调用 Reset() 办法后,persistent_str 对象就不再无效,其所援用的 JavaScript 对象也会被垃圾回收器回收。

17. 能够了解为调用永恒句柄的 Reset 函数,如果形参是空,那么就将这个永恒句柄至有效了,期待 v8 的垃圾回收吗

Persistent 句柄的 Reset() 办法用于销毁该句柄对象。如果 Reset() 办法的参数为空,那么该句柄对象就会变成有效的,期待 V8 引擎的垃圾回收器回收。如果 Reset() 办法的参数不为空,那么该句柄对象就会援用新的 JavaScript 对象。在 C++ 扩大中,能够通过以下形式调用 Reset() 办法:
void MyFunction() {v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, "Hello, world!");
  v8::Persistent<v8::String> persistent_str(isolate, str);
  // ...
  persistent_str.Reset();}

在下面的代码中,咱们首先创立了一个 HandleScope 对象,而后创立了一个 Local 句柄对象 str。接着,咱们应用 isolate 和 str 对象创立了一个 Persistent 句柄对象 persistent_str。在应用 persistent_str 对象时,咱们能够像应用 Local 句柄对象一样进行操作。在 persistent_str 对象不再须要时,咱们须要手动调用 Reset() 办法来销毁该对象。在调用 Reset() 办法后,persistent_str 对象就不再无效,其所援用的 JavaScript 对象也会被垃圾回收器回收。如果 Reset() 办法的参数为空,那么 persistent_str 对象就会变成有效的,期待 V8 引擎的垃圾回收器回收。

正文完
 0