我在我的C ++应用程序中嵌入了Spidermonkey。我需要在本机C ++中实现一些传递jsval的自定义Javascript函数。我需要保护jsval免受意外垃圾收集。我这样做是否合适:
(1)在init例程中:
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(),因为 static jsval vp 是我在两次函数调用之间传递的jsval的永久存储。并且,我继续在我的setter()中将新值设置到gc rooted静态变量vp中,假设存储在jsval中的任何先前JSObject都可用于垃圾收集。
这些正确的方法是否可以创建可以通过函数调用传递的gc rooted临时变量?特别是,我的setter()替换以前的JSObject的方式是使现有的JSObject可用于gc的正确方法(即没有内存泄漏/崩溃)。
编辑:我认为垃圾收集问题的原因是:https://developer.mozilla.org/En/SpiderMonkey/JSAPI_User_Guide
在JSAPI Concepts,Javascript Values:
部分下jsval本身并不能保护它的指示物免受垃圾的侵害 集电极
https://developer.mozilla.org/en/SpiderMonkey_Garbage_Collection_Tips
示例3说“ Root as go go ”并显示如何将jsval分配给根。
答案 0 :(得分:0)
没有必要像jsval那样添加root。您只需要维护对脚本中值的引用。 SpiderMonkey GC基于参考计数,因此只要在当前范围内引用它们,您的jsvals就不会消失:
var x = 5;
callNativeFun(X);
function foo(){throw true; }
打印(nativeFunRef());
//脚本在这里结束。 X和FOO将被垃圾收集,因为callNativeFun和nativeFunRef js函数签名。
在上面的代码示例中,x和foo都由全局对象保持引用。
只要在脚本中定义了jsval指针内容,它就永远不会到达GC。确保二进制代码在其生命结束后永远不会使用该值;也就是说,在脚本中完成它并在其上调用delete或将其值设置为未定义的值或范围终止的代理。如果您预见到任何这些负面交互,您应该在该jsval上设置GC根目录。
答案 1 :(得分:0)
参考SpiderMonkey 17及以上
如果在GC使用过程中有可能触发GC,则 jsval
或任何其他GC事物应受到保护。即使jsval
引用已受保护的存储,也需要这样做。 SpiderMonkey的GC是移动GC 。
使用RootedValue
保护基于堆的jsval
和Heap<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.