回顾,前文由node.js写的基于addon的hello world例子
#include <node.h>
#include <v8.h>
using namespace v8;
Handle<Value> Method(const Arguments& args) {
HandleScope scope;
return scope.Close(String::New("world"));
}
void init(Handle<Object> exports) {
exports->Set(String::NewSymbol("hello"),
FunctionTemplate::New(Method)->GetFunction());
}
NODE_MODULE(hello, init)
HandleScope scope
这是什么玩意,哪里才能获取到它的介绍。请留意代码段中using namespace v8
,实际上它调用了google v8的库,所以要了解node.js addon需要了解google v8引擎(以下简称v8)。
v8 是一个javascript解释引擎,跟传统javascript引擎不同,主要有四个特性
以java为例子,java编译器会先将原始代码转换成语法树,语法树编译成虚拟中间语言(字节码),java vm按顺序解释字节码,解析成机器语言。
v8编译流程,编译器将原始代码转换成语法树后,直接解释成机器语言。
传统编译流程
代码段 -> 语法树 -> 中间码 -> 机器语言
v8编译流程
代码段 -> 语法树 -> 机器语言
采用精确分代垃圾收集技术(Precise Generational GC)
年轻代,是一个较小的连续地址空间,经常进行收集。
年长代,被分成几个空间,不经常收集,先在年轻代里创立对象,没有被收集的对象被移到年长代
垃圾收集技术
传统的javascirpt引擎使用哈希表hash table来存取属性和方法,每次存取属性或找方法的时候,就会使用字符串作为寻找对象的哈希键key,搜寻哈希表是一个连续动作,首先通过散列(hashing)值判断数据内的位置,然后判断数组内的键值是否相等,如果不相等,位移到其他数组,方法比较费时。
相反,对于C++和JAVA等,编译的时候,会事先知道存储数据的类型和偏移量。
因此,V8通过隐藏类来简化索引,V8在定义对象的过程中,自动创建隐藏类,通过类的数组索引来查找对象的值。详细过程可以参考
当进行隐藏类的行为操作的时候,会被缓存,下一次采用相同的隐藏类行为的时候,能直接命中。
#include <v8.h>
using namespace v8;
int main(int argc, char* argv[]) {
// Get the default Isolate created at startup.
Isolate* isolate = Isolate::GetCurrent();
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
// Create a new context.
Handle<Context> context = Context::New(isolate);
// Here's how you could create a Persistent handle to the context, if needed.
Persistent<Context> persistent_context(isolate, context);
// Enter the created context for compiling and
// running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Handle<String> source = String::New("'Hello' + ', World!'");
// Compile the source code.
Handle<Script> script = Script::Compile(source);
// Run the script to get the result.
Handle<Value> result = script->Run();
// The persistent handle needs to be eventually disposed.
persistent_context.Dispose();
// Convert the result to an ASCII string and print it.
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
return 0;
}
Isolate表示一个独立的v8引擎实例,每个实例维护不同的状态。一个Isolate中的对象不能在其他Isolate中使用。当v8被初始化的时候,一个默认isolate被默认创建。开发者可以通过创建额外的Isolate在多线程环境下并行使用。一个Isolate任意时间只允许一个线程在其中运行,可以使用Locker和Unlocker来进行多个线程对一个Isolate的同步。
V8允许不同的JavaScript代码运行在完全不同的环境下,其运行环境称为Context。不同的Context下拥有自己的全局对象(PersistentHandle),运行代码时必须指定所在的Context。最典型的例子就是Chrome的标签,每个标签都拥有自己的Context。
Context拥有自己的全局代理对象(global proxy object),每个Context下的全局对象都是这个全局代理对象的属性。通过Context::Global ()可以得到这个全局代理对象。新建Context时你可以手动指定它的全局代理对象,这样每个Context都会自动拥有一些全局对象,比如DOM。
Context也是一种scope,通过Context::Enter ()和Context::Exit ()来进入、退出,或者使用类似于HandleScope的Context::Scope来隐式进入。
V8里使用Handle类型来托管 JavaScript对象,与C++的std::shared_pointer类似,Handle类型间的赋值均是直接传递对象引用,但不同的是,V8使用自己的GC来管理对象生命周期,而不是智能指针常用的引用计数。如果一个v8对象没有任何Handle与之相关联(不再被访问),那么这个对象很快就会被垃圾回收器回收掉。
Handle有两种类型,Local Handle和Persistent Handle,类型分别是Local : Handle和Persistent : Handle,前者和Handle没有区别,生存周期都在scope内。而后者的生命周期脱离scope,你需要手动调用Persistent::Dispose结束其生命周期。也就是说Local Handle相当于在C++在栈上分配对象,而Persistent Handle相当于C++在堆上分配对象。
一个函数中,可以有很多Handle,而HandleScope则相当于用来装Handle(Local)的容器,当HandleScope生命周期结束的时候,Handle也将会被释放,会引起Heap中对象引用的更新。HandleScope是分配在栈上,不能通过New的方式进行创建。对于同一个作用域内可以有多个HandleScope,新的HandleScope将会覆盖上一个HandleScope,并对Local Handle进行管理。
https://developers.google.com/v8/intro
http://blog.pluskid.org/?p=186