js引擎v8源码解析之zone(基于0.1.5)

2019-07-30 18:31:13 浏览数 (1)

zone也是用于内存管理的,不过他是增量分配,一次销毁的。下面是结构图。

zone.h

代码语言:javascript复制
#ifndef V8_ZONE_H_
#define V8_ZONE_H_

namespace v8 { namespace internal {


// The Zone supports very fast allocation of small chunks of
// memory. The chunks cannot be deallocated individually, but instead
// the Zone supports deallocating all chunks in one fast
// operation. The Zone is used to hold temporary data structures like
// the abstract syntax tree, which is deallocated after compilation.

// Note: There is no need to initialize the Zone; the first time an
// allocation is attempted, a segment of memory will be requested
// through a call to malloc().

// Note: The implementation is inherently not thread safe. Do not use
// from multi-threaded code.

class Zone {
 public:
  // Allocate 'size' bytes of memory in the Zone; expands the Zone by
  // allocating new segments of memory on demand using malloc().
  // 分配size大小的内存
  static inline void* New(int size);

  // Delete all objects and free all memory allocated in the Zone.
  // 一次性释放所有内存
  static void DeleteAll();

 private:
  // All pointers returned from New() have this alignment.
  static const int kAlignment = kPointerSize;

  // Never allocate segments smaller than this size in bytes.
  static const int kMinimumSegmentSize = 8 * KB;

  // Never keep segments larger than this size in bytes around.
  static const int kMaximumKeptSegmentSize = 64 * KB;


  // The Zone is intentionally a singleton; you should not try to
  // allocate instances of the class.
  // 不能new 只能调用Zone::New
  Zone() { UNREACHABLE(); }


  // Expand the Zone to hold at least 'size' more bytes and allocate
  // the bytes. Returns the address of the newly allocated chunk of
  // memory in the Zone. Should only be called if there isn't enough
  // room in the Zone already.
  // 扩展内存
  static Address NewExpand(int size);


  // The free region in the current (front) segment is represented as
  // the half-open interval [position, limit). The 'position' variable
  // is guaranteed to be aligned as dictated by kAlignment.
  // 管理内存的首地址和大小限制
  static Address position_;
  static Address limit_;
};


// ZoneObject is an abstraction that helps define classes of objects
// allocated in the Zone. Use it as a base class; see ast.h.
// 对Zone的封装new ZoneObject();即Zone::New(size);
class ZoneObject {
 public:
  // Allocate a new ZoneObject of 'size' bytes in the Zone.
  void* operator new(size_t size) { return Zone::New(size); }

  // Ideally, the delete operator should be private instead of
  // public, but unfortuately the compiler sometimes synthesizes
  // (unused) destructors for classes derived from ZoneObject, which
  // require the operator to be visible. MSVC requires the delete
  // operator to be public.

  // ZoneObjects should never be deleted individually; use
  // Zone::DeleteAll() to delete all zone objects in one go.
  // 禁止delete该类的对象
  void operator delete(void*, size_t) { UNREACHABLE(); }
};

/*
  管理allow_allocation_字段,
  new AssertNoZoneAllocation的时候,保存当前的allow_allocation_,
  设置allow_allocation_为false,析构后,恢复allow_allocation_的值
*/ 
class AssertNoZoneAllocation {
 public:
  AssertNoZoneAllocation() : prev_(allow_allocation_) {
    allow_allocation_ = false;
  }
  ~AssertNoZoneAllocation() { allow_allocation_ = prev_; }
  static bool allow_allocation() { return allow_allocation_; }
 private:
  bool prev_;
  static bool allow_allocation_;
};


// The ZoneListAllocationPolicy is used to specialize the GenericList
// implementation to allocate ZoneLists and their elements in the
// Zone.
class ZoneListAllocationPolicy {
 public:
  // Allocate 'size' bytes of memory in the zone.
  static void* New(int size) {  return Zone::New(size); }

  // De-allocation attempts are silently ignored.
  static void Delete(void* p) { }
};


// ZoneLists are growable lists with constant-time access to the
// elements. The list itself and all its elements are allocated in the
// Zone. ZoneLists cannot be deleted individually; you can delete all
// objects in the Zone by calling Zone::DeleteAll().
template<typename T>
// ZoneList本质上是一个list,ZoneListAllocationPolicy是list里的内存管理器
class ZoneList: public List<T, ZoneListAllocationPolicy> {
 public:
  // Construct a new ZoneList with the given capacity; the length is
  // always zero. The capacity must be non-negative.
  explicit ZoneList(int capacity)
      : List<T, ZoneListAllocationPolicy>(capacity) { }
};


} }  // namespace v8::internal

#endif  // V8_ZONE_H_

zone-inl.h

代码语言:javascript复制
#ifndef V8_ZONE_INL_H_
#define V8_ZONE_INL_H_

#include "zone.h"

namespace v8 { namespace internal {


inline void* Zone::New(int size) {
  ASSERT(AssertNoZoneAllocation::allow_allocation());
  // Round up the requested size to fit the alignment.
  size = RoundUp(size, kAlignment);

  // Check if the requested size is available without expanding.
  // 当前的指针位置
  Address result = position_;
  /*
    一开始position和limit都是0,所以会分配一个segment,后续还需要分配的时候,如果segment
    里的内存还可以满足,则不需要再分配一个新的segment,在原来的分配就行,
    分配size后超过了限制,扩容的时候也是分配一个segment
  */
  if ((position_  = size) > limit_) result = NewExpand(size);

  // Check that the result has the proper alignment and return it.
  ASSERT(IsAddressAligned(result, kAlignment, 0));
  return reinterpret_cast<void*>(result);
}


} }  // namespace v8::internal

#endif  // V8_ZONE_INL_H_

zone.cc

代码语言:javascript复制
#include "v8.h"

#include "zone-inl.h"

namespace v8 { namespace internal {


Address Zone::position_ = 0;
Address Zone::limit_ = 0;

bool AssertNoZoneAllocation::allow_allocation_ = true;


// Segments represent chunks of memory: They have starting address
// (encoded in the this pointer) and a size in bytes. Segments are
// chained together forming a LIFO structure with the newest segment
// available as Segment::head(). Segments are allocated using malloc()
// and de-allocated using free().

class Segment {
 public:
  // 下一个节点
  Segment* next() const { return next_; }
  // 断开指向下一个节点的指针
  void clear_next() { next_ = NULL; }
  // 内存总大小
  int size() const { return size_; }
  // 内存可用大小,前面有一个Segment对象
  int capacity() const { return size_ - sizeof(Segment); }
  // 内存的开始地址,即Segement对象
  Address start() const { return address(sizeof(Segment)); }
  // 结束地址即首地址加上size
  Address end() const { return address(size_); }
  // 返回第一个Segment节点
  static Segment* head() { return head_; }
  static void set_head(Segment* head) { head_ = head; }

  // Creates a new segment, sets it size, and pushes it to the front
  // of the segment chain. Returns the new segment.
  // 新增一个Segment
  static Segment* New(int size) {
    Segment* result = reinterpret_cast<Segment*>(Malloced::New(size));
    // 分配成功
    if (result != NULL) {
      // 头插入插入链表,size是分配的总大小
      result->next_ = head_;
      result->size_ = size;
      head_ = result;
    }
    return result;
  }

  // Deletes the given segment. Does not touch the segment chain.
  // 释放segment节点
  static void Delete(Segment* segment) {
    Malloced::Delete(segment);
  }

 private:
  // Computes the address of the nth byte in this segment.
  // 首地址加上n个字节
  Address address(int n) const {
    return Address(this)   n;
  }
  // 管理所有segment节点的头指针
  static Segment* head_;
  // 每个segment节点的属性
  Segment* next_;
  int size_;
};


Segment* Segment::head_ = NULL;


void Zone::DeleteAll() {
#ifdef DEBUG
  // Constant byte value used for zapping dead memory in debug mode.
  static const unsigned char kZapDeadByte = 0xcd;
#endif

  // Find a segment with a suitable size to keep around.
  Segment* keep = Segment::head();
  // 到末节点或者小于kMaximumKeptSegmentSize大小的节点
  while (keep != NULL && keep->size() > kMaximumKeptSegmentSize) {
    keep = keep->next();
  }

  // Traverse the chained list of segments, zapping (in debug mode)
  // and freeing every segment except the one we wish to keep.
  Segment* current = Segment::head();
  // 处理keep节点,其余节点的内存都被释放
  while (current != NULL) {
    Segment* next = current->next();
    if (current == keep) {
      // Unlink the segment we wish to keep from the list.
      current->clear_next();
    } else {
#ifdef DEBUG
      // Zap the entire current segment (including the header).
      memset(current, kZapDeadByte, current->size());
#endif
      Segment::Delete(current);
    }
    current = next;
  }

  // If we have found a segment we want to keep, we must recompute the
  // variables 'position' and 'limit' to prepare for future allocate
  // attempts. Otherwise, we must clear the position and limit to
  // force a new segment to be allocated on demand.
  // 更新属性,有保留的内存则用于下次分配
  if (keep != NULL) {
    Address start = keep->start();
    position_ = RoundUp(start, kAlignment);
    limit_ = keep->end();
#ifdef DEBUG
    // Zap the contents of the kept segment (but not the header).
    memset(start, kZapDeadByte, keep->capacity());
#endif
  } else {
    position_ = limit_ = 0;
  }

  // Update the head segment to be the kept segment (if any).
  // 更新头指针
  Segment::set_head(keep);
}


Address Zone::NewExpand(int size) {
  // Make sure the requested size is already properly aligned and that
  // there isn't enough room in the Zone to satisfy the request.
  ASSERT(size == RoundDown(size, kAlignment));
  ASSERT(position_   size > limit_);

  // Compute the new segment size. We use a 'high water mark'
  // strategy, where we increase the segment size every time we expand
  // except that we employ a maximum segment size when we delete. This
  // is to avoid excessive malloc() and free() overhead.
  Segment* head = Segment::head();
  int old_size = (head == NULL) ? 0 : head->size();
  int new_size = sizeof(Segment)   kAlignment   size   (old_size << 1);
  if (new_size < kMinimumSegmentSize) new_size = kMinimumSegmentSize;
  // 分配一个新的segment节点插入到链表
  Segment* segment = Segment::New(new_size);
  if (segment == NULL) V8::FatalProcessOutOfMemory("Zone");

  // Recompute 'top' and 'limit' based on the new segment.
  Address result = RoundUp(segment->start(), kAlignment);
  // 更新属性,下次分配的时候使用
  position_ = result   size;
  limit_ = segment->end();
  ASSERT(position_ <= limit_);
  return result;
}


} }  // namespace v8::internal

0 人点赞