在用cocos2d-javascript binding作游戏的时候遇到一个莫名其妙崩溃的问题,调用堆栈显示是在GC的时候,执行一个叫做MarkObjectRoot的函数,发生了EXC_BAD_ACCESS错误。
于是查找了些关于ObjectRoot的信息,这东西主要是为了防止SpiderMonkey执行GC操作的时候把还在使用的Object引用清除掉而存在的,不少地方都在第一次使用Object的时候调用了JS_AddNamedObjectRoot/JS_AddObjectRoot函数,并在释放Object的时候调用JS_RemoveObjectRoot函数。
联系到前面的错误,很有可能是在什么地方使用Object的时候调用了AddNamedObjectRoot/JS_AddObjectRoot函数,但是在释放的时候没有调用相应的JS_RemoveObjectRoot函数,于是SpiderMonkey并不知道这个Object已经被释放了,GC的时候仍然对它执行了MarkObjectRoot操作,从而引发了这个crash。
虽然找到了方向,但是cocos2d-x代码中用到Add/Remove ObjectRoot的地方实在太多了,一个个查起来非常的浪费时间,于是反过来使用排除法,把代码注释到不再出现这个问题为止,然后一段一段地还原,找到了这个问题跟addCollisionHandler方法有关。查看本地代码,__jsb_cpSpace_addCollisionHandler这个方法里面确实调用了JS_AddNamedObjectRoot函数,然而JSB_cpSpace_finalize方法里面也调用了相应的JS_RemoveObjectRoot函数,按理说应该没问题才对。
接着打断点调试,addCollisionHandler方法是在每一关初始化物理环境的时候调用的,这次为了调试方便,我在一关玩完后会执行一次强制GC,发现从第一次玩初始化物理环境,到重玩本关的时候第二次初始化物理环境时(也就是第二次调用addCollisionHandler时),再到第二次玩完本关GC时,JSB_cpSpace_finalize函数都一直没有被调用到,而就在第二次GC的时候,程序崩溃。看来跟我前面猜得差不多,CollisionHandler在第一次GC的时候已经被释放掉了,但是SpiderMonkey不知道,于是第二次GC的时候直接崩溃,在代码里面手动添加了removeCollsionHandler之后,果然没问题了。