前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >js引擎v8源码解析之对象第四篇(基于v8 0.1.5)

js引擎v8源码解析之对象第四篇(基于v8 0.1.5)

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

1 SemiSpace

SemiSpace是管理新生代内存的类。

代码语言:javascript
复制
// 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")
};

下面是实现

代码语言:javascript
复制
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的时候再探讨。

代码语言:javascript
复制
// 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相关的功能,后续再分析。

代码语言: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_);
}

// 设置需要管理的地址空间,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。下面看一下内存分配的函数。

代码语言: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);
  }
  // 地址+低一位的标记
  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;
}

}

内存管理,主要是通过开始指针、结束指针、指向当前可分配的内存的指针来进行管理。每次分配内存都会修改当前指针的值。

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

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

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

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

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