1. 程式人生 > >神之門V8(1):解密handle

神之門V8(1):解密handle

我們知道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中的分配以及垃圾回收演算法,敬請期待~