HandleScope背后的设计理念是什么?

时间:2012-03-01 04:55:10

标签: v8

V8需要​​声明HandleScope才能清除在范围内创建的任何Local句柄。我知道HandleScope将取消引用这些句柄进行垃圾收集,但我很感兴趣为什么每个Local类都不会像大多数内部ref_ptr类型的助手那样自行解除引用。

我的想法是,HandleScope可以通过一次性转储大量句柄而不是像在ref_ptr类型作用域类中那样逐个转储来更有效地完成它。

2 个答案:

答案 0 :(得分:6)

以下是我理解the documentationhandles-inl.h源代码的方法。我也可能完全错了,因为我不是V8开发人员,文档很少。

垃圾收集器有时会将内容从一个内存位置移动到另一个内存位置,并且在一次扫描期间,还会检查哪些对象仍然可以访问,哪些不可访问。与像std::shared_ptr这样的引用计数类型相比,它能够检测和收集循环数据结构。为了实现这一切,V8必须对可以访问的对象有一个很好的了解。

另一方面,在某些计算的内部,对象被创建和删除了很多。对于每个此类操作,您不需要太多开销。实现这一目标的方法是创建一堆句柄。在某些C ++计算中,该堆栈中列出的每个对象都可以从某个句柄中获得。除此之外,还有持久句柄,可能需要更多的工作来设置,并且可以在C ++计算之外生存。

拥有一堆引用需要您以类似堆栈的方式使用它。该堆栈中没有“无效”标记。堆栈底部到顶部的所有对象都是有效的对象引用。确保这一点的方法是LocalScope。它使事物层次化。使用引用计数指针,您可以执行以下操作:

shared_ptr<Object>* f() {
    shared_ptr<Object> a(new Object(1));
    shared_ptr<Object>* b = new shared_ptr<Object>(new Object(2));
    return b;
}
void g() {
    shared_ptr<Object> c = *f();
}

首先创建对象1,然后创建对象2 然后,然后函数返回并且对象1被销毁,然后对象2被销毁。这里的关键点是对象1无效但对象2仍然有效的时间点。这是LocalScope旨在避免的内容。

其他一些GC实现检查C堆栈并查找它们在那里找到的指针。这很容易出现误报,因为实际上数据的东西可能被误解为指针。对于可达性,这可能看起来相当无害,但是当你重新移动对象时重写指针,这可能是致命的。它还有许多其他缺点,并且很大程度上依赖于语言的低级实现如何实际工作。 V8通过将句柄堆栈与函数调用堆栈分开来避免这种情况,同时确保它们充分对齐以保证所提到的层次结构要求。

提供另一个比较:只有一个shared_ptr引用的对象在其C ++块作用域结束后变为可收集(实际上将被收集)。当离开包含v8::Handle对象的最近的封闭范围时,HandleScope引用的对象将变为可收集对象。因此,程序员可以更好地控制堆栈操作的粒度。在性能很重要的紧密循环中,为整个计算维护一个HandleScope可能很有用,这样您就不必经常访问句柄堆栈数据结构。另一方面,这样做会在计算的整个持续时间内保持所有对象,如果这是一个迭代多个值的循环,那将非常糟糕,因为所有这些都将保持到最后。但是程序员可以完全控制,并且可以以最恰当的方式安排事情。

就个人而言,我确保构建HandleScope

  • 在每个可能从代码外部调用的函数的开头。这可确保您的代码自行清理。
  • 在每个循环的主体中,可能会看到超过三次左右的迭代,因此您只能保留当前迭代中的变量。
  • 围绕每个代码块,然后进行一些回调调用,因为这样可以确保在回调需要更多内存时可以清理你的东西。
  • 每当我觉得某些东西可能会产生大量的中间数据时,应该尽快清理(或至少可以收集)。

一般情况下,如果我可以确定调用此函数的其他所有函数都已设置HandleScope,则不会为每个内部函数创建HandleScope。但那可能是一种品味问题。

答案 1 :(得分:3)

  

免责声明:这可能不是官方的答案,更多的是我的关键;但v8文档很难   对此主题有用。所以我可能被证明是错的。

从我的理解,开发各种基于v8的支持应用程序。它是处理C ++和javaScript环境之间差异的一种方法。

想象一下以下序列,自解除引用指针可能会破坏系统。

  
      
  1. JavaScript调用C ++包装的v8函数:让我们说helloWorld()
  2.   
  3. C ++函数创建一个值为“hello world = x”
  4. 的v8 ::句柄   
  5. C ++将值返回给v8虚拟机
  6.   
  7. C ++函数通常会清理资源,包括解除引用句柄
  8.   
  9. 另一个C ++函数/进程,覆盖释放的内存空间
  10.   
  11. V8读取句柄:数据不再相同“地狱!@(#...”
  12.   

这只是两者之间复杂不一致的表面;因此,为了解决将 JavaScript VM(虚拟机)连接到 C ++接口代码的各种问题,我相信开发团队决定通过以下方式简化问题。 ..

  
      
  • 所有变量句柄存储在“buckets”又名HandleScopes 中,构建/编译/运行/销毁   相关的 C ++代码
  •   
  • 另外所有函数句柄仅引用C ++静态函数(我知道这很烦人),这确保了“存在”   无论构造函数/析构函数如何,函数调用。
  •   

从开发的角度考虑,它标志着JavaScript VM开发团队和C ++集成团队(Chrome开发团队?)之间的非常强烈的区别。允许双方工作而不互相干扰。

最后,为了简单起见,也可以模拟多个虚拟机:因为v8最初是用于谷歌浏览器。因此,每当我们打开/关闭选项卡时,只需创建和销毁一个简单的HandleScope,更容易进行GC管理,尤其是在运行许多VM的情况下(Chrome中的每个选项卡)。