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; } }
內存管理,主要是通過開始指針、結束指針、指向當前可分配的內存的指針來進行管理。每次分配內存都會修改當前指針的值。
