v8的HeapObject解析

一.HeapObject之內存結構
v8使用HeapObject作爲js object的基類,其優點是一方面可以加快訪問速度,另外還可以通過垃圾回收進行管理,所有從HeapObject派生的類,都是原始的struct結構,它使用四字節作爲一個字段,第一個四字節字段是map指針,指向一個map對象,當然這個map對象也是從HeapObject派生的,我們可以從這樣的派生類定義中看到其內存結構。以StringObject爲例,可參看 StringObject的內存佈局 JSReceiver及其派生類的內存佈局

二.HeapObject中字段的設置與獲取
HeapObject及其派生類對象中的字段是靠getter和setter來獲取和設置的,但是在他們的定義中卻找不到getter和setter函數,那麼它們定義在哪裏呢?
在objects.h中,宏DECL_ACCESSORS定義如下:
#define DECL_ACCESSORS(name, type)                                      \
  inline type* name();                                                  \
  inline void set_##name(type* value,                                   \
                         WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
每個HeapObject中必然有該宏來聲明getter和setter函數

在objects-inl.h,宏ACCESSORS定義了getter和setter函數
#define ACCESSORS(holder, name, type, offset)                           \
  type* holder::name() { return type::cast(READ_FIELD(this, offset)); } \
  void holder::set_##name(type* value, WriteBarrierMode mode) {         \
    WRITE_FIELD(this, offset, value);                                   \
    CONDITIONAL_WRITE_BARRIER(GetHeap(), this, offset, value, mode);    \
  }
另外,在該頭文件中,還有大量類使用ACCESSORS宏定義其getter和setter函數
該宏中使用了READ_FIELD和WRITE_FIELD宏來讀寫指定offset的內容
#define FIELD_ADDR(p, offset) \
  (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)

#define READ_FIELD(p, offset) \
  (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)))

#define WRITE_FIELD(p, offset, value) \
  (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)

三.TypeChecker and Cast
由於使用了Struct的內存結構,無法像C++那樣在運行期保證Object的類型,v8定義瞭如下的宏來保證類型和轉換的正確性(在objects-inl.h中定義)
#define TYPE_CHECKER(type, instancetype)                                \
  bool Object::Is##type() {                                             \
  return Object::IsHeapObject() &&                                      \
      HeapObject::cast(this)->map()->instance_type() == instancetype;   \
  }
判斷type是否是instancetype

#define CAST_ACCESSOR(type)                     \
  type* type::cast(Object* object) {            \
    ASSERT(object->Is##type());                 \
    return reinterpret_cast<type*>(object);     \
  }
把Object指針強制轉換爲type*類型

我們可以看到這裏使用了map的instance_type進行實例的類型判斷,它們實際上定義了兩類函數,TYPE_CHECKER宏爲Object類定義IsType函數,CAST_ACCESSOR宏爲不同的類定義cast函數,同樣在該文件中,若干個類使用了這兩個宏來定義IsType和cast函數

四.注意事項
這裏特別需要提到的一點是在計算地址的時候,使用到了kHeapObjectTag,該值爲1,而且在調試過程中我們發現這裏用到計算地址的this指針都不是四字節對齊的,顯然在要求四字節對齊的C/C++語言中是不可想象的,所以這個this指針一定會是做了處理,通過分析我們發現在創建Struct對象的時候,Heap分配內存後,會調用HeapObject::FromAddress函數,給四字節對齊加上一個kHeapObjectTag,所以在這裏計算地址的時候,要減去這個kHeapObjectTag,否則會發生訪問違例。
HeapObject* HeapObject::FromAddress(Address address) {
  ASSERT_TAG_ALIGNED(address);
  return reinterpret_cast<HeapObject*>(address + kHeapObjectTag);
}

Address HeapObject::address() {
  return reinterpret_cast<Address>(this) - kHeapObjectTag;
}

還有另外一點,set和get函數對成員變量操作的時候,傳入的類型都是指針,需要特別注意,因爲每個成員變量都是4字節的,所以他們被get出來,或者被set的時候,都是以指針的形式,當然這並不意味着它的值就一定是一個真實的地址,也可能是一個數值,被強轉爲指針
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章