Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

[v8] Handle、Local、Persistent

  在v8中,所有的JavaScript数据都是由GC管理的,它被定义在内部,我们不能从接口直接操作数据。由于接口并没有公开内部的Scope类,我们就没有真正意义上的JavaScript变量,只有C++变量存储的Handle。为了便于一些操作,Handle就需要再派生出子类来应对更多情况。
  Handle的概念可以视为一个普通的指针,它既不影响GC也不影响HandleScope。所有数据型(Value的派生类)变量的地址(Value*)本身就是一个Handle。所以所谓“创建Handle”只不过是把数据型变量的地址用Handle类装起来而已。它甚至都没有New方法。在文档中虽然对构造函数的描述是“为指定的值创建一个新的Handle”,但实际上并没有创建新的,只是把数据的指针放入一个新的Handle变量中。
#include <node.h>
#pragma comment(lib,"node.lib")
using namespace v8;
int main(void){
  Local<String> s=String::New("test");
  Handle<String> h(*s);
  //下面两个输出结果是一样,说明两个变量指向同一个Handle
  printf("%d\n",h);
  printf("%d\n",s);
  return 0;
}
NODE_MODULE(test,main)
  Local和Handle比起来唯一特殊的地方就是会影响HandleScope,它在HandleScope中是栈形式的。创建的Local都会被放入顶层的HandleScope中,当一层HandleScope结束时在该层的的Handle也会被回收,也就是说Local的生存周期是HandleScope决定的。
  当我们调用一个数据型上的New方法来创建一个新数据时候它返回的就是相应的Local对象,即使我们不用一个Local变量去接收它,它也会在当前HandleScope中存在。当我们用一个变量去接收它时实际上并没有创建出新的Local对象。只有主动调用它的New方法时才创建新的Handle,我们可以给同一个数据指定上多个Local的Handle。
int main(void){
  HandleScope scope;
  //输出初始的句柄数量
  printf("%d\n",HandleScope::NumberOfHandles());
  Local<String> s=String::New("test");
  for(int i=0;i<20;i++)Local<String>::New(s);
  //比第一次输出多21个(String::New也创建了一个)
  printf("%d\n",HandleScope::NumberOfHandles());
  //关闭HandleScope
  scope.Close(Undefined());
  //比上一次输出少了20个(scope.Close的返回值也占用一个)
  printf("%d\n",HandleScope::NumberOfHandles());
  return 0;
}
  总之,我们的变量只是个容器而已,只有调用New方法才创建了Handle,而且这个Handle的具体类型是是接口决定的(所有数据型的New方法创建的Handle都是Local)。但是如果所有Handle的生存周期都由HandleScope来管理的话我们就无法实现一些Closure之类的效果了,所以还需要一些不由HandleScope管理的独立的Handle。这就是Persistent。
  如果HandleScope视为栈,那么Local就是堆变量。而HandleScope之外的离散区域就是堆的概念,那么Persistent就相当于堆变量。当然在v8内部有个Heap相关的封装,它是真正意义上的堆。我说的这个堆只是从JavaScript的逻辑上的堆。
  当我们需要一个不会因为HandleScope结束就被释放掉的Handle时就需要自己创建,因为所有数据型的New方法返回的都是Local。具体的创建方法也是调用New,就是Persistent的New方法。
#include <node.h>
#pragma comment(lib,"node.lib")
using namespace v8;

Handle<String> g;

Handle<Value> callback(const Arguments&){
  HandleScope scope;
  return g;
};

int main(Handle<Object> exports){
  HandleScope scope;
  g=String::New("test");
  g=Persistent<String>::New(g); //这是必须的
  auto f=FunctionTemplate::New(callback)->GetFunction();
  exports->Set(String::NewSymbol("f"),f);
  return 0;
}

NODE_MODULE(test,main)
var test=require('./test');
setInterval(function(){
  console.log(test.f());
},1000);

  只要没有手动释放掉它就一直都可以使用。如果释放掉之后在使用就会出错,比如把上面代码的callback函数改成这样。 Handle<Value> callback(const Arguments&){
  HandleScope scope;
  Persistent<String> p(g);
  p.Dispose();
  return g;
};

  总之,对于Local和Persistent把它们当做栈变量和堆变量来理解即可,而Handle这个基类则可以视为一种数据类型。
网名:
54.144.24.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^