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

2020-02-25 15:14:57 浏览数 (1)

HandleScope是一个栈式的管理方式,每次定义一个HandleScope对象的时候,就会压栈一个结构。接下来定义的Handle类都是在栈顶的那个结构中分配的。然后析构的时候,HandleScope会把管理的内存都释放掉。下面看看定义。

代码语言:javascript复制
class HandleScope {
 public:
  HandleScope() : previous_(current_), is_closed_(false) {
    current_.extensions = 0;
  }

  ~HandleScope() {
   if (!is_closed_) RestorePreviousState();
  }

  template <class T> Local<T> Close(Handle<T> value);

  static int NumberOfHandles();

  static void** CreateHandle(void* value);

 private:

  HandleScope(const HandleScope&);
  void operator=(const HandleScope&);
  void* operator new(size_t size);
  void operator delete(void*, size_t);

  class Data {
   public:
    // 分配了一块内存后,又额外分配的块数
    int extensions;
    // 下一个可用的位置
    void** next;
    // 达到limit执行的地址后说明当前内存块用完了
    void** limit;
    inline void Initialize() {
      extensions = -1;
      next = limit = NULL;
    }
  };
  // 当前的HandleScope
  static Data current_;
  // 上一个HandleScope
  const Data previous_;

  // HandleScope析构的时候调用
  void RestorePreviousState() {
    // 不释放第一块内存,如果后面需要内存时这个使用
    if (current_.extensions > 0) DeleteExtensions();
    // 出栈,指向前面那个
    current_ = previous_;
  }

  bool is_closed_;
  void** RawClose(void** value);

  // 释放额外申请的内存
  static void DeleteExtensions();

  friend class ImplementationUtilities;
};

结构图

在这里插入图片描述 HandleScope类没有很多逻辑,他主要是保存前一个scope的信息,然后把current指向自己分配的内存。我们看执行下面的代码的时候是怎样的。

代码语言:javascript复制
HandleScope scope;
Handle<String> source = String::New('hello');

我们看下Handle类的定义。

代码语言:javascript复制
template<class T>
Handle<T>::Handle(T* obj) {
  location_ = reinterpret_cast<T**>(HandleScope::CreateHandle(obj));
}

他传进来一个对象指针,然后调用HandleScope::CreateHandle。我们看看核心代码。

代码语言:javascript复制
void** v8::HandleScope::CreateHandle(void* value) {
      // 获取下一个可用的地址
      void** result = current_.next;
    if (result == current_.limit) {
      // 申请一块内存,存到一个List里。返回申请到的地址的首地址
      result = thread_local.GetSpareOrNewBlock();
      thread_local.Blocks()->Add(result);
      // 申请了第一块后,又额外申请的块数
      current_.extensions  ;
      // 申请的内存大小,指向尾地址
      current_.limit = &result[i::kHandleBlockSize];
    }
  }

  // 下一个可用的地址
  current_.next = result   1;
  *result = value;
  return result;
}

CreateHandle函数的功能是申请一块内存(如果还没有的话),然后next和limit保存这块内存的信息,把传进来的对象指针存到申请到的内存里。

在这里插入图片描述 接着CreateHandle返回保存一个地址(保存了用户定义的对象的地址),赋值给Handle对象的location属性。location对象是T**类型。 我们接着看一下HandleScope析构的时候会怎样。

代码语言:javascript复制
  void RestorePreviousState() {
    // 不释放第一块内存,如果后面需要内存时这个使用
    if (current_.extensions > 0) DeleteExtensions();
    // 出栈,指向前面那个
    current_ = previous_;
   }

首先删除当前HandleScope申请的内存,然后出栈当前的HandleScope。恢复上一个。我们看看DeleteExtensions的核心代码。

代码语言:javascript复制
void v8::HandleScope::DeleteExtensions() {
  ASSERT(current_.extensions != 0);
  thread_local.DeleteExtensions(current_.extensions);
}

void HandleScopeImplementer::DeleteExtensions(int extensions) {

  for (int i = extensions; i > 1; --i) {
    // 返回删除的元素
    void** block = blocks.RemoveLast();
    DeleteArray(block);
  }
}

block就是图中的List,每个HandleScope记住自己申请的内存块数,然后析构的时候,会释放对应的内存。从而释放堆上的对象。而Handle对象本身是在栈上分配的,也会被析构。如下图。

在这里插入图片描述 这就是v8中的HandleScope的大致原理。

0 人点赞