首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HandleScope背后的设计原理是什么?

HandleScope背后的设计原理是什么?
EN

Stack Overflow用户
提问于 2012-03-01 04:55:09
回答 2查看 3.9K关注 0票数 21

V8需要声明一个HandleScope,以便清除在作用域中创建的任何本地句柄。我理解HandleScope将取消这些句柄的垃圾收集,但我感兴趣的是为什么每个Local不像大多数内部ref_ptr类型帮助程序那样自行解除引用。

我的想法是,HandleScope可以通过一次性转储大量句柄来提高效率,而不是像在ref_ptr类型作用域类中那样一个接一个地转储句柄。

EN

回答 2

Stack Overflow用户

发布于 2015-04-07 15:30:10

下面是我对文献资料handles-inl.h源代码的理解。我也可能完全错了,因为我不是V8开发人员,而且文档很少。

垃圾收集器有时会将东西从一个内存位置移动到另一个内存位置,在一个这样的扫描过程中,还会检查哪些对象仍然可以访问,哪些对象不能。与std::shared_ptr这样的引用计数类型相比,它能够检测和收集循环数据结构。为了使所有这些都能工作,V8必须对哪些对象是可访问的有一个很好的了解。

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

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

代码语言:javascript
复制
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。但这可能是品味的问题。

票数 13
EN

Stack Overflow用户

发布于 2012-04-24 04:34:48

免责声明:这可能不是一个正式的答案,更多的是我自己的问题;但是v8文档在这个主题上几乎没有用。所以我可能被证明是错的。

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

想象一下下面的顺序,自解除引用指针可以破坏系统.

  1. JavaScript调用一个C++包装的v8函数:假设helloWorld()
  2. C++函数创建一个值为"hello =x“的V8::句柄
  3. C++将值返回给v8虚拟机。
  4. C++函数通常对资源进行清理,包括取消句柄的引用。
  5. 另一个C++函数/进程,覆盖释放的内存空间
  6. V8读取句柄:数据不再是“见鬼!”(#.)

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

  • 所有变量句柄,都是存储在“桶”中的,即HandleScopes,在需要时由构建/编译/运行/销毁各自的C++代码。
  • 另外,所有函数句柄,都是只引用C++静态函数(我知道这很烦人),这确保了函数调用的“存在”,而不管构造函数/析构函数。

从开发的角度来考虑这个问题,在中,它标志着JavaScript VM开发团队和C++集成团队(Chrome?)之间有很大的区别。允许双方在不相互干扰的情况下开展工作。

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9510822

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档