js引擎v8源碼解析之對象第四篇(基於v8 0.1.5)

  • 2019 年 11 月 24 日
  • 筆記

1 SemiSpace

SemiSpace是管理新生代內存的類。

// SemiSpace in young generation  //  // A semispace is a contiguous chunk of memory. The mark-compact collector  // uses the memory in the from space as a marking stack when tracing live  // objects.    class SemiSpace  BASE_EMBEDDED {   public:    // Creates a space in the young generation. The constructor does not    // allocate memory from the OS.  A SemiSpace is given a contiguous chunk of    // memory of size 'capacity' when set up, and does not grow or shrink    // otherwise.  In the mark-compact collector, the memory region of the from    // space is used as the marking stack. It requires contiguous memory    // addresses.    SemiSpace(int initial_capacity, int maximum_capacity);      // Sets up the semispace using the given chunk.    bool Setup(Address start, int size);      // Tear down the space.  Heap memory was not allocated by the space, so it    // is not deallocated here.    void TearDown();      // True if the space has been set up but not torn down.    bool HasBeenSetup() { return start_ != NULL; }        bool Double();      // Returns the start address of the space.    Address low() { return start_; }    // Returns one past the end address of the space.    Address high() { return low() + capacity_; }      // Age mark accessors.    Address age_mark() { return ag偏移_mark_; }    void set_age_mark(Address mark) { age_mark_ = mark; }      // True if the address is in the address range of this semispace (not    // necessarily below the allocation pointer).    // 判斷地址a是否在該對象管理的內存中,&address_mask即讓a減去size-1的大小。如果等於start說明在管理範圍內    bool Contains(Address a) {      return (reinterpret_cast<uint32_t>(a) & address_mask_)             == reinterpret_cast<uint32_t>(start_);    }      // True if the object is a heap object in the address range of this    // semispace (not necessarily below the allocation pointer).    // 類似上面的邏輯,但是堆對象低位是標記,判斷時候需要處理一下,加SetUp    bool Contains(Object* o) {      return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;    }      // The offset of an address from the begining of the space.    // 距離開始地址的p    int SpaceOffsetForAddress(Address addr) { return addr - low(); }     private:    // The current and maximum capacity of the space.    int capacity_;    int maximum_capacity_;      // The start address of the space.    Address start_;    // Used to govern object promotion during mark-compact collection.    Address age_mark_;      // Masks and comparison values to test for containment in this semispace.    // 見SetUp函數    uint32_t address_ma函數    uint32_t object_mask_;    uint32_t object_expected_;     public:    TRACK_MEMORY("SemiSpace")  };

下面是實現

SemiSpace::SemiSpace(int initial_capacity, int maximum_capacity)      : capacity_(initial_capacity), maximum_capacity_(maximum_capacity),        start_(NULL), age_mark_(NULL) {  }    // 設置管理的地址範圍  bool SemiSpace::Setup(Address start, int size) {    ASSERT(size == maximum_capacity_);    // 判斷地址的有效性    if (!MemoryAllocator::CommitBlock(start, capacity_)) return false;    // 管理地址空間的首地址    start_ = start;    // 低於有效範圍的掩碼,即保證相與後的值小於等於管理的地址範圍    address_mask_ = ~(size - 1);    // 計算對象地址掩碼,低位是標記位,判斷的時候需要保留    object_mask_ = address_mask_ | kHeapObjectTag;    // 見contains函數,對象地址里低位是標記位,判斷的時候需要帶上    object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;    // gc相關    age_mark_ = start_;    return true;  }  ja    void SemiSpace::TearDown() {    start_ = NULL;    capacity_ = 0;  }    // 擴容  bool SemiSpace::Double() {    if (!MemoryAllocator::CommitBlock(high(), capacity_)) return false;    capacity_ *= 2;    return true;  }

SemiSpace他自己不申請內存。他是負責管理某塊內存的,內存申請在其他地方處理。

2 NewSpace

NewSpace也是管理新生代內存的類。新生代內存分為兩半,一個是from區,一個是to區。具體的作用在分析gc的時候再探討。

// The young generation space.  //  // The new space consists of a contiguous pair of semispaces.  It simply  // forwards most functions to the appropriate semispace.    class NewSpace : public Malloced {   public:      NewSpace(int initial_semispace_capacity, int maximum_semispace_capacity);      bool Setup(Address start, int size);    void TearDown();      // True if the space has been set up but not torn down.     bool HasBeenSetup() {      return to_space_->HasBeenSetup() && from_space_->HasBeenSetup();    }      // Flip the pair of spaces.    void Flip();      bool Double();      bool Contains(Address a) {      return (reinterpret_cast<uint32_t>(a) & address_mask_)          == reinterpret_cast<uint32_t>(start_);    }    bool Contains(Object* o) {      return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;    }      // Return the allocated bytes in the active semispace.    // to區已分配的內存大小    int Size() { return top() - bottom(); }    // Return the current capacity of a semispace.    int Capacity() { return capacity_; }    // Return the available bytes without growing in the active semispace.    // to區還有多少內存可用    int Available() { return Capacity() - Size(); }      // Return the maximum capacity of a semispace.    int MaximumCapacity() { return maximum_capacity_; }      // Return the address of the allocation pointer in the active semispace.    // 當前已經分配出去的內存的末地址    Address top() { return allocation_info_.top; }    // Return the address of the first object in thkeyoctive semispace.    // to_space的管理的內存的首地址    Address bottom() { return to_space_->low(); }      // Get the age mark of the inactive semispace.    Address age_mark() { return from_space_->age_mark(); }    // Set the age mark in the active semispace.    void set_age_mark(Address mark) { to_space_->set_age_mark(mark); }      // The start address of the space and a bit mask. Anding an address in the    // new space with the mask will result in the start address.    Address start() { return start_; }    uint32_t mask() { return address_mask_; }      // The allocation top and limit addresses.    // 當前已分配的內存的末地址    Address* allocation_top_address() { return &allocation_info_.top; }    // 最大能分配的內存末地址    Address* allocation_limit_address() { return &allocation_info_.limit; }      Object* AllocateRaw(int size_in_bytes) {      return AllocateRawInternal(size_in_bytes, &allocation_info_);    }      Object* MCAllocateRaw(int size_in_bytes) {      return AllocateRawInternal(size_in_bytes, &mc_forwarding_info_);    }      void ResetAllocationInfo();      void MCResetRelocationInfo();      void MCCommitRelocationInfo();      // Get the extent of the inactive semispace (for use as a marking stack).    Address FromSpaceLow() { return from_space_->low(); }    Address FromSpaceHigh() { return from_space_->high(); }      // Get the extent of the active semispace (to sweep newly copied objects    // during a scavenge collection).    Address ToSpaceLow() { return to_space_->low(); }    Address ToSpaceHigh() { return to_space_->high(); }      // Offsets from the beginning of the semispaces.    int ToSpaceOffsetForAddress(Address a) {      return to_space_->SpaceOffsetForAddress(a);    }    int FromSpaceOffsetForAddress(Address a) {      return from_space_->SpaceOffsetForAddress(a);    }      bool ToSpaceContains(Object* o) { return to_space_->Contains(o); }    bool FromSpaceContains(Object* o) { return from_space_->Contains(o); }      bool ToSpaceContains(Address a) { return to_space_->Contains(a); }    bool FromSpaceContains(Address a) { return from_space_->Contains(a); }      void RecordAllocation(HeapObject* obj);    void RecordPromotion(HeapObject* obj);  #endif     private:    // The current and maximum capacities of a semispace.    int capacity_;    int maximum_capacity_;      // The semispaces.    SemiSpace* to_space_;    SemiSpace* from_space_;      // Start address and bit mask for containment testing.    Address start_;    uint32_t address_mask_;    uint32_t object_mask_;    uint32_t object_expected_;      // Allocation pointer and limit for normal allocation and allocation during    // mark-compact collection.    AllocationInfo allocation_info_;    AllocationInfo mc_forwarding_info_;      // Implementation of AllocateRaw and MCAllocateRaw.    inline Object* AllocateRawInternal(int size_in_bytes,                                       AllocationInfo* alloc_info);      friend class SemiSpaceIterator;     public:    TRACK_MEMORY("NewSpace")  };

newSpace的很多功能但是靠semiSpace來實現的。他負責內存的具體分配。但不負責內存的申請。還有些是和gc相關的功能,後續再分析。

// 分為兩個space  NewSpace::NewSpace(int initial_semispace_capacity,                     int maximum_semispace_capacity) {    ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);    ASSERT(IsPowerOf2(maximum_semispace_capacity));    maximum_capacity_ = maximum_semispace_capacity;    capacity_ = initial_semispace_capacity;    to_space_ = new SemiSpace(capacity_, maximum_capacity_);    from_space_ = new SemiSpace(capacity_, maximum_capacity_);  }    // 設置需要管理的地址空間,start是首地址,size是大小  bool NewSpace::Setup(Address start, int size) {    ASSERT(size == 2 * maximum_capacity_);    ASSERT(IsAddressAligned(start, size, 0));    // to區    if (to_space_ == NULL        || !to_space_->Setup(start, maximum_capacity_)) {      return false;    }    // from區,和to區一人一半    if (from_space_ == NULL        || !from_space_->Setup(start + maximum_capacity_, maximum_capacity_)) {      return false;    }    // 開始地址    start_ = start;    /*      address_mask的高位是地址的有效位,      size是只有一位為一,減一後一變成0,一右邊      的全部0位變成1,然後取反,高位的0變成1,再加上size中本來的1,      即從左往右的1位地址有效位    */    address_mask_ = ~(size - 1);    // 參考semiSpace的分析    object_mask_ = address_mask_ | kHeapObjectTag;    object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;    // 初始化管理的地址的信息    allocation_info_.top = to_space_->low();    allocation_info_.limit = to_space_->high();    mc_forwarding_info_.top = NULL;    mc_forwarding_info_.limit = NULL;      ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);    return true;  }    // 重置屬性,不負責內存的釋放  void NewSpace::TearDown() {      start_ = NULL;    capacity_ = 0;    allocation_info_.top = NULL;    allocation_info_.limit = NULL;    mc_forwarding_info_.top = NULL;    mc_forwarding_info_.limit = NULL;      if (to_space_ != NULL) {      to_space_->TearDown();      delete to_space_;      to_space_ = NULL;    }      if (from_space_ != NULL) {      from_space_->TearDown();      delete from_space_;      from_space_ = NULL;    }  }    // 翻轉,在gc中調用  void NewSpace::Flip() {    SemiSpace* tmp = from_space_;    from_space_ = to_space_;    to_space_ = tmp;  }    // 擴容  bool NewSpace::Double() {    ASSERT(capacity_ <= maximum_capacity_ / 2);    // TODO(1240712): Failure to double the from space can result in    // semispaces of different sizes.  In the event of that failure, the    // to space doubling should be rolled back before returning false.    if (!to_space_->Double() || !from_space_->Double()) return false;    capacity_ *= 2;    // 從新擴容的地址開始分配內存,即老內存的末端。    allocation_info_.limit = to_space_->high();    ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);    return true;  }    // 重置管理內存分配的指針  void NewSpace::ResetAllocationInfo() {    allocation_info_.top = to_space_->low();    allocation_info_.limit = to_space_->high();    ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);  }      void NewSpace::MCResetRelocationInfo() {    mc_forwarding_info_.top = from_space_->low();    mc_forwarding_info_.limit = from_space_->high();    ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);  }      void NewSpace::MCCommitRelocationInfo() {    // Assumes that the spaces have been flipped so that mc_forwarding_info_ is    // valid allocation info for the to space.    allocation_info_.top = mc_forwarding_info_.top;    allocation_info_.limit = to_space_->high();    ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);  }

我們看到實現里沒有很多具體的邏輯,只是對屬性進行操作,或者把操作下發到semiSpace。下面看一下內存分配的函數。

// 分配內存  Object* NewSpace::AllocateRawInternal(int size_in_bytes,                                        AllocationInfo* alloc_info) {      Address new_top = alloc_info->top + size_in_bytes;    // 內存不夠了    if (new_top > alloc_info->limit) {      return Failure::RetryAfterGC(size_in_bytes, NEW_SPACE);    }    // 地址+低一位的標記    Object* obj = HeapObject::FromAddress(alloc_info->top);    // 更新指針,指向下一塊可分配的內存    alloc_info->top = new_top;  #ifdef DEBUG    SemiSpace* space =        (alloc_info == &allocation_info_) ? to_space_ : from_space_;    ASSERT(space->low() <= alloc_info->top           && alloc_info->top <= space->high()           && alloc_info->limit == space->high());  #endif    return obj;  }    }

內存管理,主要是通過開始指針、結束指針、指向當前可分配的內存的指針來進行管理。每次分配內存都會修改當前指針的值。