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

aug*_*hey 17 v8

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

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

MvG*_*MvG 8

以下是我对文档handles-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();
}
Run Code Online (Sandbox Code Playgroud)

这里首先创建对象1,然后创建对象2,然后函数返回并且对象1被破坏,然后对象2被销毁.这里的关键点是对象1无效但对象2仍然有效的时间点.这就是LocalScope要避免的目标.

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

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

就个人而言,我一定要建一个 HandleScope

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

一般情况下,我不会HandleScope为每个内部函数创建一个,如果我可以确定调用它的每个其他函数已经设置了一个HandleScope.但那可能是品味问题.


Pic*_*tor 3

免责声明:这可能不是官方答案,更多的是我个人的想法;但 v8 文档在这个主题上几乎没有用处。所以我可能被证明是错的。

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

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

  1. JavaScript 调用 C++ 包装的 v8 函数:让我们说 helloWorld()
  2. C++ 函数创建一个值为“hello world =x”的 v8::handle
  3. C++ 将值返回给 v8 虚拟机
  4. C++ 函数执行通常的资源清理工作,包括句柄的取消引用
  5. 另一个C++函数/进程,覆盖释放的内存空间
  6. V8 读取句柄:并且数据不再相同“hell!@(#...”

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

  • 所有变量句柄都将存储在“存储桶”(又名 HandleScopes)中,并在需要时由各自的C++ 代码构建/编译/运行/销毁
  • 此外,所有函数句柄仅引用 C++ 静态函数(我知道这很烦人),这确保了函数调用的“存在”,无论构造函数/析构函数如何。

从开发的角度来看,它标志着 JavaScript VM 开发团队和 C++ 集成团队(Chrome 开发团队?)之间的显着区别。让双方能够互不干扰地工作。

最后,也可能是为了简单起见,模拟多个虚拟机:因为 v8 最初是为 google chrome 设计的。因此,每当我们打开/关闭选项卡时,简单的 HandleScope 创建和销毁都会使 GC 管理变得更加容易,特别是在运行多个 VM 的情况下(chrome 中的每个选项卡)。