3

我将 Spidermonkey 嵌入到我的 C++ 应用程序中。我需要在本机 C++ 中实现一些自定义 Javascript 函数,这些函数传递一个 jsval。我需要保护 jsval 免受意外垃圾收集。我这样做是否合适:

(1) 在初始化例程中:

static jsval vp; // a STATIC variable, value unknown
JSBool init((JSContext *cx, uintN argc, jsval *vp) {
   JS_AddValueRoot(cx,  &vp);
}

(2) 在一个实现 Javascript 函数 setter() 的 c++ 函数中:

JSBool setter(JSContext *cx, uintN argc, jsval *vp) {
   ...
  vp=...;// set to some JSObject and hopefully makes any previous JSObject available for gc

}

(3) 在实现 Javascript 函数 getter() 的同一编译单元内的第二个 C++ 函数调用中:

JSBool getter(JSContext *cx, uintN argc, jsval *vp) {
  jsval somethingelse = vp; //directly retrieve the static value stored by setter()
  ....
}

我的 Javascript 脚本使用如下函数调用:

init();
setter(...);
some_other_function_call_that_causes_gc();
getter();
setter(...);
some_other_function_call_that_causes_gc();
getter();
....
some_other_function_call_that_causes_gc();
setter(...);
some_other_function_call_that_causes_gc();
getter();

请注意,我从不调用 JS_RemoveRoot(),因为静态 jsval vp是在 2 个函数调用之间传递的我的 jsval 的永久存储。而且,我不断在我的 setter() 中为 gc 根静态变量 vp 设置新值,假设存储在 jsval 中的任何 previouis JSObject 都可用于垃圾收集。

这些是创建可以跨函数调用传递的 gc 根临时变量的正确方法吗?特别是,我的 setter() 替换以前的 JSObject 的方式是使现有 JSObject 可用于 gc 的正确方法(即没有内存泄漏/崩溃)。

编辑:我认为垃圾收集是一个问题的原因是:

https://developer.mozilla.org/En/SpiderMonkey/JSAPI_User_Guide

在 JSAPI 概念、Javascript 值部分下:

jsval 本身并不能保护其所指对象免受垃圾收集器的影响

https://developer.mozilla.org/en/SpiderMonkey_Garbage_Collection_Tips

示例 3 说“随你去的根”,并展示了如何将 jsval 分配给根。

4

2 回答 2

0

不需要为 jsval 之类的东西添加根。您只需要在脚本中维护对值的引用。SpiderMonkey GC 是基于引用计数的,因此只要在当前范围内引用它们,您的 jsval 就不会消失:

变量 x = 5;

调用NativeFun(x);

函数 foo() { 抛出真;}

打印(nativeFunRef());

//脚本到此结束。X 和 FOO 将像 callNativeFun 和 nativeFunRef js 函数签名一样被垃圾收集。

在上面的代码示例中,x 和 foo 都被全局对象引用。

只要在脚本中定义了 jsval 指针内容,它就永远不会到达 GC。确保您的二进制代码在其生命周期结束后永远不会使用该值;也就是说,当您在脚本中完成它并对其调用 delete 或将其值设置为未定义的值或范围终止代理时。如果您预见到任何这些负面交互,您应该在该 jsval 上设置 GC 根。

于 2012-03-31T07:27:32.177 回答
0

参考 SpiderMonkey 17 及更高版本

jsval如果在使用 GC 事物时有机会触发 GC,则应保护任何其他 GC 事物。即使jsval指的是已经受保护的存储,这也是必需的。SpiderMonkey 的 GC 是一个移动的 GC

用于RootedValue保护基于堆栈jsvalHeap<JS::Value>基于堆jsval
jsval与 相同JS::Value)。使用Handle<JS::Value>MutableHandle<JS::Value>作为函数的参数。

以下是 RootingAPI 评论的摘录:

 * A moving GC may change the physical location of GC allocated things, even
 * when they are rooted, updating all pointers to the thing to refer to its new
 * location. The GC must therefore know about all live pointers to a thing,
 * not just one of them, in order to behave correctly.
 *
 * For a code fragment such as:
 *
 * JSObject *obj = NewObject(cx);
 * DoSomething(cx);
 * ... = obj->lastProperty();
 *
 * If |DoSomething()| can trigger a GC, the stack location of |obj| must be
 * rooted to ensure that the GC does not move the JSObject referred to by
 * |obj| without updating |obj|'s location itself. This rooting must happen
 * regardless of whether there are other roots which ensure that the object
 * itself will not be collected.
 *
 * If |DoSomething()| cannot trigger a GC, and the same holds for all other
 * calls made between |obj|'s definitions and its last uses, then no rooting
 * is required.
 *
 * SpiderMonkey can trigger a GC at almost any time and in ways that are not
 * always clear. For example, the following innocuous-looking actions can
 * cause a GC: allocation of any new GC thing; JSObject::hasProperty;
 * JS_ReportError and friends; and ToNumber, among many others. The following
 * dangerous-looking actions cannot trigger a GC: js_malloc, cx->malloc_,
 * rt->malloc_, and friends and JS_ReportOutOfMemory.
于 2014-03-03T02:07:10.267 回答