JSObjectRef root unroot gc问题

CC_UNUSED bool ok = true;
se::ValueArray args;
args.resize(1);
ok &= Data_to_seval(larg0, &args[0]);
se::Value rval;
se::Object* thisObj = jsThis.isObject() ? jsThis.toObject() : nullptr;
se::Object* funcObj = jsFunc.toObject();
bool succeed = funcObj->call(args, thisObj, &rval);

请问 bool succeed = funcObj->call(args, thisObj, &rval); 这句代码执行前 args[0] 里的 JSObjectRef 有没有可能被jsc gc?
这个 JSObjectRef 对象 在执行完这一行 ok &= Data_to_seval(larg0, &args[0]); 代码后应该是unroot状态对吗?

麻烦引擎的大哥们帮忙解答一些,非常感谢!

@jare @Knox

1赞

JSObjectRef 本身有引用计数,不会被GC掉,是否 unroot 状态,你可以调试下,一般情况都是unroot状态。

多谢回复!

我在这里遇到一个crash麻烦参考一下这个帖子
https://forum.cocos.com/t/ios-glteximage2d-exc-bad-access/74364

如果我在本帖代码里 Data_to_seval 下一行加代码 args[0].toObject()->root();
在 bool succeed = funcObj->call(args, thisObj, &rval); 代码后面加代码 args[0].toObject()->unroot();
就不会出现链接帖子里的crash了

我打log看就是在Data_to_seval之后bool succeed = funcObj->call(args, thisObj, &rval);这句代码之前在Data_to_seval里申请的内存被free了。

还有一个crash发生在jsb_global_load_image这个函数里
也是Data_to_seval调用之后 再调用callbackVal.toObject()->call(seArgs, nullptr);的时候crash了
这个几率更低更难重现

我看保存JSObjectRef的那个Object对象是有引用计数的
JSObjectRef本身也是有引用计数的吗

看GC的文档说
JavaScript values that are on the machine stack, in a register, protected by JSValueProtect, set as the global object of an execution context, or reachable from any such value will not be collected.
现在这种情况应该属于上述哪种情况才能保证不被GC呢

老铁 你说的这两个crash,我们的项目里也存在,出现机率比较低,请问按照你上面说的修改可以解决吗?jsb_global_load_image这个函数里的crash有解决的办法吗

我也是刚改完,现在看是不crash了。不过几率低所以也不好说就一定改好了。
你可以试一下看看。主要现在官方也没确定是这个问题,所以我也不敢保证。

jsb_global_load_image 这里就按照上面那个改就行。
把这个代码se::Value dataVal;从括号里拿出来。
Data_to_seval 之后dataVal.toObject()->root();
callbackVal.toObject()->call(seArgs, nullptr);之后dataVal.toObject()->unroot();

感谢!我先改着测试下。出现概率低,但确实存在,很头疼。

希望有用:grin:

这就两句代码,看不出可被释放的过程,你说申请的内存指的是什么对象的内存?

我简单写一下

js_CanvasRenderingContext2D_setCanvasBufferUpdatedCallback这个函数里
调用Data_to_seval
调用createTypedArray
调用void* copiedData = malloc(byteLength);这里申请了一块内存
继续往下在这里JSObjectRef jsobj = JSObjectMakeTypedArrayWithBytesNoCopy(__cx, jscTypedArrayType, copiedData, byteLength, myJSTypedArrayBytesDeallocator, nullptr, &exception);生成了JSObjectRef jsobj这个对象
jsobj被放到Object* obj对象里return出来
被放到se::HandleObject obj里这里会root一下
然后obj被放到返回值ret里返回上一层
刚才的se::HandleObject obj这时候析构又unroot了一下,这时候刚才的jsobj是unroot状态
这个ret在外层是se::ValueArray args;里的args[0],如下

ok &= Data_to_seval(larg0, &args[0]);
//log1
se::Value rval;
se::Object* thisObj = jsThis.isObject() ? jsThis.toObject() : nullptr;
se::Object* funcObj = jsFunc.toObject();
//log2
bool succeed = funcObj->call(args, thisObj, &rval);

这段代码在//log1和//log2之间也就是,最后一句代码内部执行JSObjectCallAsFunction之前。
有可能会释放之前malloc的copiedData的内存
我在JSObjectMakeTypedArrayWithBytesNoCopy这个函数的myJSTypedArrayBytesDeallocator这个函数里打了log,如下
static void myJSTypedArrayBytesDeallocator(void* bytes, void* deallocatorContext)
{
//log bytes的地址
free(bytes);
}
发现这个地址和刚才malloc的copiedData的地址是同一个地址

1赞

厉害了,如果检测到释放,那一定就是被释放了,能定位下释放时的堆栈吗?按理是同一线程,可以看出是什么原因释放的,我们这边试试能捕捉到这个崩溃不。

这个没测试了
没有在myJSTypedArrayBytesDeallocator这个函数里面打过断点,只是打了log输出free的地址
个人感觉像是js那边在gc
一个原因是因为输出这个地址free的log的时候,还会连续输出很多个其他地址free的log,像是释放了很多资源
另一个原因是因为这个bug出现的概率很低可能是要刚好赶上js gc的时机
只是猜测,oc和js交互这块儿确实是不太懂

根据这个猜测,我试了一下先root一下能不能临时解决问题,目前看像是解决了

重现这个崩溃需要找性能比较差内存比较小的设备会更容易点儿
最终大部分是挂在glTexImage2D那里了

@panda iOS版本,5%-10%概率随机crash的问题,管一下吧。别做了小游戏就不管原生app了

2.0.5 ios原生,报了很多jsb_global_load_image崩溃


下个项目准备用unity了,cocos现在都不管原生了

我这样改了一下

请问下是否改动过cocos2dx-lite的代码?可以尝试修改这个代码,如果问题依然存在,请尽量把使用的内存上限控制在IOS设备可接受范围,避免内存回收导致的崩溃。

在报这个崩溃之前没改过cocos2dx-lite jsc这块代码。现在也仅仅是改了global_load_image这里。
我先观察线上版本还有没类似崩溃,然后再试试你说的这个修改

1赞

我们在 2.0.10 和 2.1.1 里面修复了大量的原生崩溃问题。这里描述的问题应该已经修复了,详见 https://github.com/cocos-creator/cocos2d-x-lite/pull/1716