js引擎v8源码分析之Handle(基于v8 0.1.5)

2024-03-27 21:32
文章标签 分析 源码 js 引擎 0.1 v8 handle

本文主要是介绍js引擎v8源码分析之Handle(基于v8 0.1.5),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Handle是使用v8的时候很重要的一个概念和类。他本质是堆对象的封装。我们通过Handle管理真正的对象,而不是直接操作对象。Handle在v8中有两个实现。一个是对外使用的一个是内部使用的。我们先看一下内部使用的。

1 内部handle

template<class T>
class Handle {public:INLINE(Handle(T** location))  { location_ = location; }INLINE(explicit Handle(T* obj));INLINE(Handle()) : location_(NULL) {}template <class S> Handle(Handle<S> handle) {location_ = reinterpret_cast<T**>(handle.location());}INLINE(T* operator ->() const)  { return operator*(); }bool is_identical_to(const Handle<T> other) const {return operator*() == *other;}INLINE(T* operator*() const);T** location() const {return location_;}template <class S> static Handle<T> cast(Handle<S> that) {T::cast(*that);return Handle<T>(reinterpret_cast<T**>(that.location()));}static Handle<T> null() { return Handle<T>(); }bool is_null() {return location_ == NULL; }inline Handle<T> EscapeFrom(HandleScope* scope);private:T** location_;
};

下面是实现。

template<class T>
Handle<T>::Handle(T* obj) {location_ = reinterpret_cast<T**>(HandleScope::CreateHandle(obj));
}template <class T>
inline T* Handle<T>::operator*() const {return *location_;
}

Handle类的定义没有太多的逻辑,就是对用户定义的对象指针进行封装。有一个重要的地址是构造函数。我们看到当我们定义一个Handle的时候,他会调HandleScope::CreateHandle生成一个Handle对象。在HandleScope那篇文章已经分析过了。handle对象的location对象指针一个内存,该内存保存了obj的地址。

2 外部handle

// T表示handle管理的对象的类型
template <class T> class Handle {public:Handle();explicit Handle(T* val) : val_(val) { }// *that得到指向handle管理的对象的指针,转成T类型,赋值给val_template <class S> inline Handle(Handle<S> that): val_(reinterpret_cast<T*>(*that)) {TYPE_CHECK(T, S);}bool IsEmpty() { return val_ == 0; }T* operator->();T* operator*();void Clear() { this->val_ = 0; }/*比较handle指向的对象的地址是否相等this是指向当前对象的指针,*this是当前对象,**this是返回val_的值,看重载运算符*的实现*that是val_的值*/template <class S> bool operator==(Handle<S> that) {void** a = reinterpret_cast<void**>(**this);void** b = reinterpret_cast<void**>(*that);// a等于0,则返回b是否等于0,是的话说明a==b,即trueif (a == 0) return b == 0;// a不等于0,如果b==0,则返回falseif (b == 0) return false;// 比较ab,即取val_里的内容比较return *a == *b;}template <class S> bool operator!=(Handle<S> that) {return !operator==(that);}template <class S> static inline Handle<T> Cast(Handle<S> that) {// 返回一个空的handle,即val_是nullif (that.IsEmpty()) return Handle<T>();// *that得到指向handle管理的对象的指针,转成T类型的对象,转成底层对象是类型T的handlereturn Handle<T>(T::Cast(*that));}private:T* val_;
};

下面是实现。只有两个运算符的重载。

template <class T>
T* Handle<T>::operator->() {return val_;
}template <class T>
T* Handle<T>::operator*() {return val_;
}

我们看到Handle的实现没有太内容,就是在对象和用户之前加了一层。下面看看他的两个子类Local和Persistent。

3 Local

1 Local类是基于栈分配的一种Handle,他在一个函数开始的时候,声明一个HandleScope,HandleScope下面所有的Handle都在最近的的HandleScope中分配,函数执行完后,会一起被释放。

template <class T> class Local : public Handle<T> {public:Local();// 调用Local函数的时候S被替换成that对应的类型,结果是Handle底层的val_指向一个T类型的对象template <class S> inline Local(Local<S> that)// *that即取得他底层对象的地址: Handle<T>(reinterpret_cast<T*>(*that)) {TYPE_CHECK(T, S);}template <class S> inline Local(S* that) : Handle<T>(that) { }template <class S> static inline Local<T> Cast(Local<S> that) {if (that.IsEmpty()) return Local<T>();return Local<T>(T::Cast(*that));}static Local<T> New(Handle<T> that);
};

Local没有做什么事情,是对基类Handle的简单继承。下面是实现。

template <class T>
Handle<T>::Handle() : val_(0) { }template <class T>
Local<T>::Local() : Handle<T>() { }template <class T>
Local<T> Local<T>::New(Handle<T> that) {if (that.IsEmpty()) return Local<T>();void** p = reinterpret_cast<void**>(*that);return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(*p)));
}

我们看看如果使用一个句柄。

HandleScope scope;
Local<String> source = String::New('hello');

我们看一下String::New的实现。

// i::Handle表示内部使用的handle
Local<String> v8::String::New(const char* data, int length) {if (length == -1) length = strlen(data);// 申请一个对象,由handle管理i::Handle<i::String> result = i::Factory::NewStringFromUtf8(i::Vector<const char>(data, length));return Utils::ToLocal(result);
}Local<v8::String> Utils::ToLocal(v8::internal::Handle<v8::internal::String> obj) { return Local<String>(reinterpret_cast<String*>(obj.location())); 
}

我们在看下HandleScope中的那个图。再来例假ToLocal函数的逻辑。
在这里插入图片描述
我们对着图来理解ToLocal,我们知道obj.location()返回的是指针,指向保存了对象地址的内存地址。然后转成String*,即拿到对象的地址。构造一个Local对象返回。即Local内部管理用户定义的对象(String::New函数执行完后,他里面定义的result,即handle被析构)。如下图。
在这里插入图片描述
当HandleScope析构的时候,他会释放用户定义的对象的内存,然后Local对象本身是在栈上分配的,也会被析构。这就是v8用本地handle(临时handle)管理堆对象的大致原理。一般来说handle在函数结束后就会被释放,如果想在函数执行完还使得句柄可用,可用使用逃逸(escape)。原理是销毁当前的HandleScope,然后在前一个HandleScope对象里分配一个handle。下面继续看看持久句柄。
4 Persisten

template <class T> class Persistent : public Handle<T> {public:Persistent();template <class S> inline Persistent(Persistent<S> that): Handle<T>(reinterpret_cast<T*>(*that)) {TYPE_CHECK(T, S);}template <class S> inline Persistent(S* that) : Handle<T>(that) { }template <class S> explicit inline Persistent(Handle<S> that): Handle<T>(*that) { }template <class S> static inline Persistent<T> Cast(Persistent<S> that) 		{if (that.IsEmpty()) return Persistent<T>();return Persistent<T>(T::Cast(*that));}static Persistent<T> New(Handle<T> that);void Dispose();void MakeWeak(void* parameters, WeakReferenceCallback callback);void ClearWeak();bool IsNearDeath();bool IsWeak();private:friend class ImplementationUtilities;friend class ObjectTemplate;
};

相对于基类Handle,Persistent多了几个功能,我们看一下使用用例。

Persistent<Context> context = Context::New();

我们看一下Context::New()的定义。

Persistent<Context> v8::Context::New(v8::ExtensionConfiguration* extensions,v8::Handle<ObjectTemplate> global_template,v8::Handle<Value> global_object) {i::Handle<i::Context> env = i::Bootstrapper::CreateEnvironment(Utils::OpenHandle(*global_object),global_template, extensions);return Persistent<Context>(Utils::ToLocal(env));
}

我们看一下CreateEnvironment的实现。

Handle<Context> Bootstrapper::CreateEnvironment(...参数) {Genesis genesis(global_object, global_template, extensions);return genesis.result();
}Genesis::Genesis(...参数) {CreateRoots(global_template, global_object);result_ = global_context_;}void Genesis::CreateRoots(...参数) {// 创建一个全局上下文对象,分配一个Context对象global_context_ =Handle<Context>::cast(GlobalHandles::Create(*Factory::NewGlobalContext()));
}Handle<Context> result() { return result_; }

通过上面的代码我们知道Persistent指向的是一个GlobalHandles::Create返回的地址。所以我们主要分析GlobalHandles这个类的实现。这个类的代码比较多,我们只分析相关的(Node类维护一个对象的信息,地址,状态)。后面会单独分析。

// 一个handle对应一个Node
Handle<Object> GlobalHandles::Create(Object* value) {Counters::global_handles.Increment();Node* result;/*有一个free_list,保存着DESTROYED状态但还没有被释放的Node,first_free指向第一个节点,为NULL说明没有待回收的节点,即没有可重用的节点 */if (first_free() == NULL) {// Allocate a new node.// 没有可重用的节点则分配一个新的result = new Node(value);// 头插法,设置新增的node的下一个节点是当前头结点result->set_next(head());// 头指针指向新增的nodeset_head(result);} else {// Take the first node in the free list.// 获取一个可以重用的节点result = first_free();// 获取重用节点在free_list中的第一个节点,first_free指向新的可重用节点 set_first_free(result->next_free());// 重新初始化该节点result->Initialize(value);}// 返回Node对象的首地址return result->handle();
}

从上面的代码中我们大概知道,有一个链表,每个node节点保存了一个持久对象的信息。持久句柄指向的对象都是在这个链表里管理的。持久句柄执行node的地址,最后要调Dispose释放。

template <class T>
void Persistent<T>::Dispose() {if (this->IsEmpty()) return;V8::DisposeGlobal(reinterpret_cast<void**>(**this));
}void V8::DisposeGlobal(void** obj) {LOG_API("DisposeGlobal");if (has_shut_down) return;i::GlobalHandles::Destroy(reinterpret_cast<i::Object**>(obj));
}// 销毁一个节点
void GlobalHandles::Destroy(Object** location) {Counters::global_handles.Decrement();if (location == NULL) return;Node* node = Node::FromLocation(location);node->Destroy();// Link the destroyed.// 设置待销毁节点在free_list链表里的下一个节点是当前的头结点node->set_next_free(first_free());// 头指针指向待销毁的节点,set_first_free(node);
}

大致是根据对象的地址转成node节点,销毁该节点。从链表中删除。

总结,这就是v8中关于handle的一些知识。

这篇关于js引擎v8源码分析之Handle(基于v8 0.1.5)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/853446

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串