前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >v8的堆内存初始化

v8的堆内存初始化

作者头像
theanarkh
发布2019-11-24 00:59:02
8110
发布2019-11-24 00:59:02
举报
文章被收录于专栏:原创分享原创分享

在V8::Initialize里对堆进行了初始化

// Setup the object heap
  ASSERT(!Heap::HasBeenSetup());
  if (!Heap::Setup(create_heap_objects)) {
    has_been_setup_ = false;
    return false;
  }

下面来看Setup函数

bool Heap::Setup(bool create_heap_objects) {
  // Initialize heap spaces and initial maps and objects. Whenever something
  // goes wrong, just return false. The caller should check the results and
  // call Heap::TearDown() to release allocated memory.
  //
  // If the heap is not yet configured (eg, through the API), configure it.
  // Configuration is based on the flags new-space-size (really the semispace
  // size) and old-space-size if set or the initial values of semispace_size_
  // and old_generation_size_ otherwise.
  // 没有配置过则先设置各空间需要的大小
  if (!heap_configured) {
    if (!ConfigureHeap(FLAG_new_space_size, FLAG_old_space_size)) return false;
  }

  // Setup memory allocator and allocate an initial chunk of memory.  The
  // initial chunk is double the size of the new space to ensure that we can
  // find a pair of semispaces that are contiguous and aligned to their size.
  // 初始化内存分配器的属性,大小等于新生代和老生代大小。还没分配内存
  if (!MemoryAllocator::Setup(MaxCapacity())) return false;
  // 分配一块内存
  void* chunk
      = MemoryAllocator::ReserveInitialChunk(2 * young_generation_size_);
  if (chunk == NULL) return false;

  // Put the initial chunk of the old space at the start of the initial
  // chunk, then the two new space semispaces, then the initial chunk of
  // code space.  Align the pair of semispaces to their size, which must be
  // a power of 2.
  ASSERT(IsPowerOf2(young_generation_size_));
  // 刚才分配的空间中,老生代在开始位置
  Address old_space_start = reinterpret_cast<Address>(chunk);
  // 紧接着是新生代,算出大小是young_generation_size_的n倍,值大于old_space_start的最小值
  Address new_space_start = RoundUp(old_space_start, young_generation_size_);
  // 代码空间等于新生代开始+新生代大小
  Address code_space_start = new_space_start + young_generation_size_;
  // 老生代空间大小
  int old_space_size = new_space_start - old_space_start;
  /*
    因为chunk的空间两倍的young_generation_size_,新生代大小占了一半,
    所以还有一半,剩下的一半老生代占了old_space_size,所以剩下的代码区大小
  */
  int code_space_size = young_generation_size_ - old_space_size;
  /*
                   |young_generation_size_|   
    chunk =>  -----------------------------------
              ^    ^                      ^      ^
              |    |                      |      |
              old  new                    code   end
  */
  // Initialize new space.
  // 分配一个管理新生代地址空间的对象,传入初始值和最大值,因为新生代分配from和to,所以这两个初始化值是每个空间的属性
  new_space_ = new NewSpace(initial_semispace_size_, semispace_size_);
  if (new_space_ == NULL) return false;
  // 设置新生代对象管理的地址范围,young_generation_size_ = 2 * semispace_size_ 
  if (!new_space_->Setup(new_space_start, young_generation_size_)) return false;

  // Initialize old space, set the maximum capacity to the old generation
  // size.
  old_space_ = new OldSpace(old_generation_size_, OLD_SPACE);
  if (old_space_ == NULL) return false;
  if (!old_space_->Setup(old_space_start, old_space_size)) return false;

  // Initialize the code space, set its maximum capacity to the old
  // generation size.
  code_space_ = new OldSpace(old_generation_size_, CODE_SPACE);
  if (code_space_ == NULL) return false;
  if (!code_space_->Setup(code_space_start, code_space_size)) return false;

  // Initialize map space.
  // 存储map的空间
  map_space_ = new MapSpace(kMaxMapSpaceSize);
  if (map_space_ == NULL) return false;
  // Setting up a paged space without giving it a virtual memory range big
  // enough to hold at least a page will cause it to allocate.
  // 在SetUp里分配内存,并初始化管理内存的对象
  if (!map_space_->Setup(NULL, 0)) return false;

  lo_space_ = new LargeObjectSpace();
  if (lo_space_ == NULL) return false;
  if (!lo_space_->Setup()) return false;

  if (create_heap_objects) {
    // Create initial maps.
    if (!CreateInitialMaps()) return false;
    if (!CreateApiObjects()) return false;

    // Create initial objects
    if (!CreateInitialObjects()) return false;
  }

  LOG(IntEvent("heap-capacity", Capacity()));
  LOG(IntEvent("heap-available", Available()));

  return true;
}

我们知道v8的堆是分为新生代,老生代,大对象等区域,从代码中我们也看到内存是分为几个部分,我们一个个来看。首先看NewSpace。

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_);

  // Allocate and setup the histogram arrays if necessary.
#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
  allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
  promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);

#define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
                       promoted_histogram_[name].set_name(#name);
  INSTANCE_TYPE_LIST(SET_NAME)
#undef SET_NAME
#endif
}

NewSpace的主要代码是新建了两个SemiSpace。一个是to一个是from,这就是我们经常听说的,新生代对象在from和to区域互相转移。我们再看看SemiSpace。

SemiSpace::SemiSpace(int initial_capacity, int maximum_capacity)
    : capacity_(initial_capacity), maximum_capacity_(maximum_capacity),
      start_(NULL), age_mark_(NULL) {
}

只是初始化了一些属性。回到最开始,new了一个NewSpace后,执行了该对象的Setup函数。

// 设置需要管理的地址空间,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);
  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;
}

接着来看oldSpace。老生代是新生代对象晋升的地方。

class OldSpace : public PagedSpace {
 public:
  // Creates an old space object with a given maximum capacity.
  // The constructor does not allocate pages from OS.
  explicit OldSpace(int max_capacity, AllocationSpace id)
      : PagedSpace(max_capacity, id), free_list_(id) {
  }
  ...
 }
PagedSpace::PagedSpace(int max_capacity, AllocationSpace id) {
  ASSERT(id == OLD_SPACE || id == CODE_SPACE || id == MAP_SPACE);
  // 先算出小于max_capacity,是页大小的倍数的最大值,再除以页大小则得到页数,再乘以对象的大小则得到总大小
  max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
                  * Page::kObjectAreaSize;
  identity_ = id;
  accounting_stats_.Clear();

  allocation_mode_ = LINEAR;

  allocation_info_.top = NULL;
  allocation_info_.limit = NULL;

  mc_forwarding_info_.top = NULL;
  mc_forwarding_info_.limit = NULL;
}

OldSpace也是初始化了一些字段。然后执行Setup。

bool PagedSpace::Setup(Address start, size_t size) {
  if (HasBeenSetup()) return false;

  int num_pages = 0;
  // Try to use the virtual memory range passed to us.  If it is too small to
  // contain at least one page, ignore it and allocate instead.
  // 分配虚拟内存,算出有效的大小
  if (PagesInChunk(start, size) > 0) {
    // 分配虚拟内存
    first_page_ = MemoryAllocator::CommitPages(start, size, this, &num_pages);
  } else {
    int requested_pages = Min(MemoryAllocator::kPagesPerChunk,
                              max_capacity_ / Page::kObjectAreaSize);
    // 分配虚拟内存
    first_page_ =
        MemoryAllocator::AllocatePages(requested_pages, &num_pages, this);
    if (!first_page_->is_valid()) return false;
  }

  // We are sure that the first page is valid and that we have at least one
  // page.
  ASSERT(first_page_->is_valid());
  ASSERT(num_pages > 0);
  accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize);
  ASSERT(Capacity() <= max_capacity_);
  // 初始化page链表
  for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
    p->ClearRSet();
  }

  // Use first_page_ for allocation.
  SetAllocationInfo(&allocation_info_, first_page_);

  return true;
}

MapSpace和老生代的逻辑一样。最后新建了一个LargeObjectSpace对象。并执行Setup

LargeObjectSpace::LargeObjectSpace()
    : first_chunk_(NULL),
      size_(0),
      page_count_(0) {}

bool LargeObjectSpace::Setup() {
  first_chunk_ = NULL;
  size_ = 0;
  page_count_ = 0;
  return true;
}

最后我们来看MemoryAllocator类。首先执行了Setup。

bool MemoryAllocator::Setup(int capacity) {
  // 页的整数倍
  capacity_ = RoundUp(capacity, Page::kPageSize);

  // Over-estimate the size of chunks_ array.  It assumes the expansion of old
  // space is always in the unit of a chunk (kChunkSize) except the last
  // expansion.
  //
  // Due to alignment, allocated space might be one page less than required
  // number (kPagesPerChunk) of pages for old spaces.
  //
  // Reserve two chunk ids for semispaces, one for map space and one for old
  // space.
  // 最大的chunk数,
  max_nof_chunks_ = (capacity_ / (kChunkSize - Page::kPageSize)) + 4;
  if (max_nof_chunks_ > kMaxNofChunks) return false;

  size_ = 0;
  ChunkInfo info;  // uninitialized element.
  // 初始化chunks列表和id,max_nof_chunks_大于list的长度的话list会自动扩容
  for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
    chunks_.Add(info);
    free_chunk_ids_.Add(i);
  }
  top_ = max_nof_chunks_;
  return true;
}

chunks_和free_chunk_ids_是MemoryAllocator的属性。List类之前分析过。

 // Chunks_, free_chunk_ids_ and top_ act as a stack of free chunk ids.
  static List<ChunkInfo> chunks_;
  static List<int> free_chunk_ids_;
  static int max_nof_chunks_;
  static int top_;

chunkInfo是一个类

 class ChunkInfo BASE_EMBEDDED {
   public:
    ChunkInfo() : address_(NULL), size_(0), owner_(NULL) {}
    void init(Address a, size_t s, PagedSpace* o) {
      address_ = a;
      size_ = s;
      owner_ = o;
    }
    Address address() { return address_; }
    size_t size() { return size_; }
    PagedSpace* owner() { return owner_; }

   private:
    Address address_;
    size_t size_;
    PagedSpace* owner_;
  };

执行完Setup接着执行了ReserveInitialChunk函数。

void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
  ASSERT(initial_chunk_ == NULL);
  // 新建一个VM对象,分配size的虚拟内存,记录在VM对象
  initial_chunk_ = new VirtualMemory(requested);
  CHECK(initial_chunk_ != NULL);
  //是否已经分配了虚拟地址
  if (!initial_chunk_->IsReserved()) {
    delete initial_chunk_;
    initial_chunk_ = NULL;
    return NULL;
  }

  // We are sure that we have mapped a block of requested addresses.
  ASSERT(initial_chunk_->size() == requested);
  LOG(NewEvent("InitialChunk", initial_chunk_->address(), requested));
  size_ += requested;
  // 返回虚拟地址
  return initial_chunk_->address();
}

主要逻辑是新建了一个VM对象。VM类定义是

VirtualMemory::VirtualMemory(size_t size, void* address_hint) {
  address_ = mmap(address_hint, size, PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
                  kMmapFd, kMmapFdOffset);
  size_ = size;
}

所以一个VM类是管理一片虚拟内存的对象。ReserveInitialChunk函数最后返回分配的虚拟内存首地址。这块内存就是V8的堆内存,即新生代、老生代、大对象等堆内存都在上面。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档