v8中Heap的初始化

本文详细介绍了V8引擎中Heap的初始化过程,包括MaxReserved的计算、Space与Page的定义、Heap对象的内存分配策略,以及NewSpace、OldPointerSpace等不同空间的设置方法。着重讲解了MemoryAllocator如何分配内存,以及VirtualMemory在内存管理中的作用。
一.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类的实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值