v8中Heap的初始化

一.Heap中的size
Heap實例存在於Isolate中,它的構造函數和ConfigureHeap函數對以下一些重要的size進行了初始化:
  // Returns the maximum amount of memory reserved for the heap.  For
  // the young generation, we reserve 4 times the amount needed for a
  // semi space.  The young generation consists of two semi spaces and
  // we reserve twice the amount needed for those in order to ensure
  // that new space can be aligned to its size.
MaxReserved=4 * reserved_semispace_size_ + max_old_generation_size_
其中
reserved_semispace_size_ = 8388608 = 0x00800000 = 8M//表示reserved semispace的大小,其中reserved是在virtual memory alloc中的一個步驟
max_old_generation_size_ = 734003200 = 0x2bc00000 = 734M
max_semispace_size_ = 8388608 = 0x00800000 = 8M
initial_semispace_capacity = 1048576 = 0x00100000 = 1M
Page:: kPageSize = 1M byte
CommitPageSize=4096 byte

二.Space與Page

Space是所有存儲空間的基類,它定義了Space的id和executable,所以它的派生類必須在構造函數中說明這兩個屬性。
FixedSpace類用來存儲固定大小的對象,在構造函數中,除了說明Space的id以外,還需要指明以字節爲單位的對象大小。
Map類定義在objects.h中,MapSpace類從FixedSpace繼承,它指定的固定大小是Map::kSize,從map類的定義中我們可以看到Map對象的結構如下:

因爲Map是一個HeapObject對象,HeapObject對象的第一個字段都是Map,也就是HeapObject的head,後面每個字段的大小都是4字節,也就是說一個Map對象的大小是40字節

MemoryChunk結構
graphic
MemoryChunkspaces.h中定義,它的頭部共有 72字節,如上圖所示,然後是 32K字節用作比特位標誌,body部分是從下一個 32字節對齊處開始,爲了適應 map對象和code 對象,有需要 128字節對齊,則kObjectStartOffset從下一個 128字節對齊處開始,對於spaceid不是 CODE_SPACEspace 來說,它其中的 MemoryChunkkObjectStartOffset 開始就是 Object分配區域了,但是對於CODE_SPACE來說,爲了保護 code page,在code 區域的前後各留了一個 CommitPage,即系統的page大小 4096字節,另外還要算上CommitPage對齊的 padding,剩下的纔是用於分配給 code的區域,即從32768 + 8K處纔是真正的開始地址, Code Area的大小爲1M – (32768 + 8K) – 4K = 1003520

三.Heap::Setup()
該函數的主要流程如下:
1.new_space_.SetUp()
2.old_pointer_space.SetUp()
3.old_data_space.Setup()
4.code_space.SetUp()
5.map_space.SetUp()
6.cell_space.SetUp()
7.lo_space.SetUp()
8.store_buffer->SetUp()
下面一一進行說明
1.new_space_.SetUp()
NewSpace類繼承自Space,它包含兩個SemiSpace實例,from_space和to_space,它就是垃圾回收算法的中的Young Generation Space,它包含的兩個semispace的空間是連續的。setup函數就是負責分配NewSpace的內存的,其流程如下:
1.1 Reserve 兩倍的reserved_semispace_capacity
代碼如下,其中size = 2* reserved_semispace_capacity
Address MemoryAllocator::ReserveAlignedMemory(size_t size,
                                              size_t alignment,
                                              VirtualMemory* controller) {
  VirtualMemory reservation(size, alignment);

  if (!reservation.IsReserved()) return NULL;
  size_ += reservation.size();
  Address base = RoundUp(static_cast<Address>(reservation.address()),
                         alignment);
  controller->TakeControl(&reservation);
  return base;
}
MemoryAllocator類是專門負責reserve,commit和分配釋放內存的,MemoryAllocator分配的內存都是以Page作爲單位的,一組Page被稱爲chunk。MemoryAllocator使用VirtualMemory類負責實現reserve和commit內存。
platform.h中定義了VirtualMemory 類,它是對操作系統虛擬內存的封裝,虛擬內存最主要的作用是可以 Reserve,然後再commit Reserve就是向操作系統在虛擬地址空間中預定一塊內存,並且可以指定這塊內存的 RWX權限,commit 就是在需要使用這塊內存的時候,再讓操作系統映射真是的內存資源,這樣的好處是,在分配大內存空間的時候,可以很快完成,在真正需要內存的時候,再進行分配, VirtualMemeory類的構造函數如下:
VirtualMemory::VirtualMemory(size_t size, size_t alignment)
    : address_(NULL), size_(0) {
  ASSERT(IsAligned(alignment, static_cast<intptr_t>( OS::AllocateAlignment())));
Platform-win32.cc 文件中定義了函數 OS::AllocateAlignment(),它使用GetSystemInfo 函數得到 SYSTEM_INFO.dwAllocationGranularity ,它表示使用 virtualalloc,每次分配內存的粒度,這個值等於 65536
  size_t request_size = RoundUp(size + alignment,
                                static_cast<intptr_t>(OS::AllocateAlignment()));
對於 new space setup來說,size = alignment = 16M
  void* address = ReserveRegion(request_size);
該函數會從一個隨機地址開始,預留 32M的空間,權限是PAGE_NO_ACCESS
  if (address == NULL) return;
  Address base = RoundUp(static_cast<Address>(address), alignment);
這主要是因爲剛纔的地址是隨機生成的,並不能保證 alignment對齊,這裏再重新計算新的地址,再 reserve
  // Try reducing the size by freeing and then reallocating a specific area.
  bool result = ReleaseRegion(address, request_size);
  USE(result);
  ASSERT(result);
  address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS);
  if (address != NULL) {
    request_size = size;
    ASSERT(base == static_cast<Address>(address));
  } else {
    // Resizing failed, just go with a bigger area.
    address = ReserveRegion(request_size);
    if (address == NULL) return;
  }
  address_ = address;
  size_ = request_size;
}
這裏第一次之所以使用了隨機地址,是爲了安全考慮,防止根據固定地址進行的 hack

1.2 to_space.Setup和from_space.Setup
初始化一些變量
1.3 to_space.commit
MemoryAllocator commit to_space大小的內存。然後把to_space按照頁大小,加入到自己的頁鏈表中,其中每個頁的大小爲1M字節

2.old_pointer_space.SetUp()
old_pointer_space是OldSpace類的實例,它被初始化爲NOT_EXECUTABLE
3.old_data_space.Setup()
old_data_space是OldSpace類的實例,它被初始化爲NOT_EXECUTABLE
4.code_space.SetUp()
code_space是OldSpace類的實例,它被初始化爲EXECUTABLE
5.map_space.SetUp()
map_space是PagedSpace類的實例
6.cell_space.SetUp()
cell_space是PagedSpace類的實例
7.lo_space.SetUp()
lo_space是PagedSpace類的實例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章