神之门V8(1):解密handle<T>

来源:互联网 发布:java初学者注意什么 编辑:程序博客网 时间:2024/06/05 09:13

我们知道js跟php,java这些都可以说是托管于其他本地程序的语言,它们需要自己的一套运行环境,比如java的jvm,php有zend引擎,也有hhvm,当然本场主角v8引擎也给了js同样的东西,v8引擎号称天下第一快,背后肯定隐藏着许多不为人知的秘密,待我来一一探究,关于v8的编译优化,内联代码优化隐藏类的东西我会放在v8的另一篇文章,这里主要讲v8的内存管理。

这个话题很长久啊,无论是zend,jvm,内存管理都是个需要考虑的重要问题,毕竟当你js写的飞起是是底层的v8在为你默默的分配一块块内存,还不能忘记在适当时候进行回收,虽然v8的实例不会持续太久(在浏览器上试问有人会连续还几天停在某一个页面或者不断的刷新么),但别忘了是v8召唤出了nodejs这项伟大的技术,nodejs是可以跑在服务器上的,服务器上连续开个十天半个月要是你内存泄露突然服务器挂掉,那就是十几亿上下的损失了,所以v8对内存的管理还是比较到位的,内存管理主要是对象在v8以什么样的形态存在,怎么在内存中为我的js对象分配对象,怎么回收对象,释放内存,基本就介绍这些核心模块。

先看第一个,我在js中写 var bb = 6; 我创建了一个变量,那么v8在进行词法分析语法树解析后会知道我的意图,行,我就在内存中给你分配一块空间,很简单,new运算符就可以得到它的指针了,但这个指针什么时候该销毁呢,程序不知道了吧,所以对象在v8中可不能就这么赤裸裸一个蠢指针,得做封装成为一个智能指针,在v8中使一个handle模板类,我们来看看源代码,我会把操作符重载那些代码略去,不然是在太长,

template <class T> class Handle { public:  /**   * Creates an empty handle.   */  V8_INLINE Handle() : val_(0) {}  template <class S> V8_INLINE Handle(Handle<S> that)      : val_(reinterpret_cast<T*>(*that)) {    /**     * This check fails when trying to convert between incompatible     * handles. For example, converting from a Handle<String> to a     * Handle<Number>.     */    TYPE_CHECK(T, S);  }  V8_INLINE void Clear() { val_ = 0; }  V8_INLINE T* operator->() const { return val_; }  V8_INLINE T* operator*() const { return val_; } private: T* val_;  //这个就是我们那个原本赤裸裸的指针啦

这样y用handle一扩展,指针就强大好多了,不过强行类型转换还是觉得C++的自由,但,就把我们刚说的那个指针用一个类封装一下就智能了么,别忘了C++的继承,如果你看到只是一个基类呢,不错,为了针对不同的对象(局部变量与全局变量,甚至一些闭包中内层引用也是具有全局效果的)执行不同v8中handle一共派生出两个子类,一个叫做v8::Local< T > 另一个叫做v8::Persistent< T > 很好,不过开发者为了避免一些资源泄露问题,v.12.0版本过后的nodejs v8就不在让后者直接继承handle类了,待会看新版本的源代码就知道了,从名字上就可以猜到他们像玩什么花样了,首先第一个就是本地变量,这些变量挂在函数的scope上或者全局scope,是运行在一定实例作为的上下文(context)中的,他们不可以分配在heap中,只能分配在栈内存,然后由cpu内部的指令集进行内存的scope控释放(这个是raii技法的基础),所以这个就不劳我们费心了,主要是后者,这家伙是分配在heap上的,所以我们需要进行智能化的处理,还是看看persistent类的代码:

template <class T, class M> class Persistent : public PersistentBase<T> { public:  /**   * A Persistent with no storage cell.   */  V8_INLINE Persistent() : PersistentBase<T>(0) { }  //不忘记给父类进行内部实例指针的初始化  /**   * Construct a Persistent from a Handle.   * When the Handle is non-empty, a new storage cell is created   * pointing to the same object, and no flags are set.   */    V8_INLINE ~Persistent() {    if (M::kResetInDestructor) this->Reset();  }  }

我们就可以清晰的看到,这个类的继承关系,要智能,肯定就得跟GC产生互动,在它的父类中,声明了这么几个函数,

  V8_INLINE void MarkPartiallyDependent();  V8_INLINE bool IsIndependent() const;  /** Checks if the handle holds the only reference to an object. */  V8_INLINE bool IsNearDeath() const;  /** Returns true if the handle's reference is weak.  */  V8_INLINE bool IsWeak() const;

这是干嘛呢,其实这个类一般来说是不会被GC清理掉的,要清理得我们手动,除非被弱引用,看名字就知道GC特别喜欢欺负她,看到一堆对象,当然第一个就把弱的干掉,所以,当自己被弱引用时,GC是会对它进行追踪的,一旦时机触发(当前内存很低)就会通过若引用回调wrappedReferenceCallback函数通知主线程这个对象我忍了她很久了,是时候释放内存了,当然这个类对于弱引用的设置是可以调控的,

template<typename S, typename P>  V8_INLINE void SetWeak(      P* parameter,      typename WeakCallbackData<S, P>::Callback callback);  template<typename P>  V8_INLINE P* ClearWeak();

看到代码中是可以设定弱引用的,搞得像属性访问器一样,确定了传递给回调函数的参数,到这里,我们就清楚了一个js对象在引擎中是怎么进行内存分配,这个指针又是怎么被一层层封装的,而且又是通过跟GC交互变得smart起来的,本次分享就到这里,下一篇我将会将对象在heap中的分配以及垃圾回收算法,敬请期待~

0 0
原创粉丝点击