js引擎v8源码分析之NewSpace(基于v8 0.1.5)

2020-02-17 11:49:28 浏览数 (2)

NewSpace是v8内存管理中,负责管理新生代区的类。分为from和to两个区,每个区由SemiSpace对象管理。和SemiSpace一样,NewSpace也不负责内存的分配和释放,他只负责内存的使用和管理。下面是类的定义。

代码语言:javascript复制
class NewSpace : public Malloced {
 public:
  NewSpace(int initial_semispace_capacity, int maximum_semispace_capacity);

  bool Setup(Address start, int size);

  void TearDown();

  bool HasBeenSetup() {
    return to_space_->HasBeenSetup() && from_space_->HasBeenSetup();
  }

  void Flip();

  bool Double();
  // 判断地址a是否属于NewSpace管理的内存范围,address_mask_是用于清除a的低n位。见SetUp函数
  bool Contains(Address a) {
    return (reinterpret_cast<uint32_t>(a) & address_mask_)
        == reinterpret_cast<uint32_t>(start_);
  }
  // 判断堆对象是不是在NewSpace管理的内存里,object_mask_和object_expected_见SetUp函数
  bool Contains(Object* o) {
    return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;
  }

  // to区已分配的内存大小
  int Size() { return top() - bottom(); }

  int Capacity() { return capacity_; }

  // to区还有多少内存可用
  int Available() { return Capacity() - Size(); }

  int MaximumCapacity() { return maximum_capacity_; }

  // 当前已经分配出去的内存的末地址
  Address top() { return allocation_info_.top; }
  // to_space的管理的内存的首地址
  Address bottom() { return to_space_->low(); }

  Address age_mark() { return from_space_->age_mark(); }

  void set_age_mark(Address mark) { to_space_->set_age_mark(mark); }

  Address start() { return start_; }
  uint32_t mask() { return address_mask_; }

  // 当前已分配的内存的末地址
  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();

  Address FromSpaceLow() { return from_space_->low(); }
  Address FromSpaceHigh() { return from_space_->high(); }
  // to区管理的内存的首地址和末地址
  Address ToSpaceLow() { return to_space_->low(); }
  Address ToSpaceHigh() { return to_space_->high(); }
  // 地址a距离to区管理的内存的首地址的偏移
  int ToSpaceOffsetForAddress(Address a) {
    return to_space_->SpaceOffsetForAddress(a);
  }
  int FromSpaceOffsetForAddress(Address a) {
    return from_space_->SpaceOffsetForAddress(a);
  }
  // 见SemiSpace
  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); }

 private:
  int capacity_;
  int maximum_capacity_;

  SemiSpace* to_space_;
  SemiSpace* from_space_;

  Address start_;
  uint32_t address_mask_;
  uint32_t object_mask_;
  uint32_t object_expected_;
  /*
    struct AllocationInfo {
      Address top;  // current allocation top
      Address limit;  // current allocation limit
    };
  */
  AllocationInfo allocation_info_;
  AllocationInfo mc_forwarding_info_;

  inline Object* AllocateRawInternal(int size_in_bytes,
                                     AllocationInfo* alloc_info);

  friend class SemiSpaceIterator;

 public:
  TRACK_MEMORY("NewSpace")
};

下面看看类的实现。

NewSpace

一个NewSpace分为两个SemiSpace,真正的初始化函数是SetUp

代码语言:javascript复制
// 分为两个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_);
}

Setup

初始化NewSpace对象的属性。和from区to区。

代码语言:javascript复制
// 设置需要管理的地址空间,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);
  // 用于判断堆对象是不是在NewSpace管理的内存范围
  object_mask_ = address_mask_ | kHeapObjectTag;
  object_expected_ = reinterpret_cast<uint32_t>(start) | kHeapObjectTag;
  // 初始化管理的地址的信息,to区管理的开始地址和结束地址
  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;
}

TearDown

重置属性,不负责内存的释放

代码语言:javascript复制
// 重置属性,不负责内存的释放
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();
    // 释放SemiSpace对象
    delete to_space_;
    to_space_ = NULL;
  }

  if (from_space_ != NULL) {
    from_space_->TearDown();
    delete from_space_;
    from_space_ = NULL;
  }
}

Flip

翻转from区和to区。

代码语言:javascript复制
// 翻转,在gc中调用
void NewSpace::Flip() {
  SemiSpace* tmp = from_space_;
  from_space_ = to_space_;
  to_space_ = tmp;
}

Double

代码语言:javascript复制
// 扩容
bool NewSpace::Double() {
  ASSERT(capacity_ <= maximum_capacity_ / 2);
  if (!to_space_->Double() || !from_space_->Double()) return false;
  capacity_ *= 2;
  // 从新扩容的地址开始分配内存,即之前分配的内存的末端。
  allocation_info_.limit = to_space_->high();
  return true;
}

还有一些重置属性的函数。在垃圾回收的时候再具体分析他的作用。

代码语言:javascript复制
// 重置管理内存分配的指针
void NewSpace::ResetAllocationInfo() {
  allocation_info_.top = to_space_->low();
  allocation_info_.limit = to_space_->high();
}

void NewSpace::MCResetRelocationInfo() {
  mc_forwarding_info_.top = from_space_->low();
  mc_forwarding_info_.limit = from_space_->high();
}


void NewSpace::MCCommitRelocationInfo() {

  allocation_info_.top = mc_forwarding_info_.top;
  allocation_info_.limit = to_space_->high();
}

AllocateRawInternal

代码语言:javascript复制
// 分配内存
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);
  }
  // 地址 低一位的标记,转成堆对象的地址表示,即低位置1
  Object* obj = HeapObject::FromAddress(alloc_info->top);
  // 更新指针,指向下一块可分配的内存
  alloc_info->top = new_top;
  return obj;
}

0 人点赞