关于 Spidermonkey v52 升级问题的咨询

Cocos Creator 1.6 版本中将 Spidermonkey 升级到了 v52 大大的提升了 jsb 在 安卓上的性能。

由于我们现在的项目用的还是 cocos2d-x (js) 框架,所以有几个问题想咨询一下官方:

  1. 官方近期是否有将 cocos2d-x 框架中的 Spidermonkey 升级的计划

  2. 如果我自己通过参考 Creator 的 lite 引擎中的 jsb 层的修改,并将其移植到 cocos2d-x 中,想问下这个难度有多大

谢谢 …

这个工作量还是很大的,-x可能会直接升级到JSB 2.0,就是封装了各个JS引擎的接口,同时也升级了SpiderMonkey。不过短期内没有资源做这块,而且2.0也还没完成。如果你自己移植的话,一下是panda的给一个国外开发者的建议


It could be quite a challenge to upgrade Spidermonkey, they modify many APIs and design through v33 to v52. We have achieved it in Cocos2d-x-lite used by Cocos Creator, you can refer to that repo to see what we have done.

Briefly, you need to do the following

1. Upgrade the spider monkey builds, we built it for win32(vs2015), mac os, iOS, android, you can find it all here

https://github.com/cocos-creator/cocos2d-x-lite-external

for each platform, you will need the headers in include/spidermonkey/ and libjs_static.a & libmozglue.a (this is new in v52) in libs/

2. Upgrade bindings-generator with this branch, this can help to generate all the auto bindings directly

https://github.com/cocos-creator/bindings-generator/tree/runtime

3. Upgrade all the manual bindings, this is the hardest part, you can compare the cocos2d/scripting/js-bindings/manual/ folder between cocos2d-x and cocos2d-x-lite (v1.6 branch) to see the difference

https://github.com/cocos-creator/cocos2d-x-lite/tree/v1.6

We are not making the upgrade for chipmunk bindings, because we removed it from creator, if you are not using it neither, great news, otherwise, it's also a heavy task because all chipmunk bindings can only be upgraded manually.
The merge process could be hard, so you can refer to the PRs we made step by step: 

Major one:
https://github.com/cocos-creator/cocos2d-x-lite/pull/675

Bug fixes:
https://github.com/cocos-creator/cocos2d-x-lite/pull/682
https://github.com/cocos-creator/cocos2d-x-lite/pull/703
https://github.com/cocos-creator/cocos2d-x-lite/pull/704
https://github.com/cocos-creator/cocos2d-x-lite/pull/705
https://github.com/cocos-creator/cocos2d-x-lite/pull/718
https://github.com/cocos-creator/cocos2d-x-lite/pull/719
https://github.com/cocos-creator/cocos2d-x-lite/pull/720
https://github.com/cocos-creator/cocos2d-x-lite/pull/726
https://github.com/cocos-creator/cocos2d-x-lite/pull/727
https://github.com/cocos-creator/cocos2d-x-lite/pull/728
https://github.com/cocos-creator/cocos2d-x-lite/pull/729
https://github.com/cocos-creator/cocos2d-x-lite/pull/730
https://github.com/cocos-creator/cocos2d-x-lite/pull/731
https://github.com/cocos-creator/cocos2d-x-lite/pull/733
https://github.com/cocos-creator/cocos2d-x-lite/pull/739
https://github.com/cocos-creator/cocos2d-x-lite/pull/742
https://github.com/cocos-creator/cocos2d-x-lite/pull/743
https://github.com/cocos-creator/cocos2d-x-lite/pull/745
https://github.com/cocos-creator/cocos2d-x-lite/pull/747
https://github.com/cocos-creator/cocos2d-x-lite/pull/748

There are also many PRs about project adjustment, please just refer to the history in repo.

4. Last one, if you want to use jsc compiler, you need to use binaries in this repo

https://github.com/cocos2d/console-binary/tree/creator/plugins/plugin_jscompile/bin

感谢官方的回复。

我尝试了一下自己移植合并,但确实是比较困难,因为牵涉的点很多,所以暂时放弃了。

但是这里还有一个问题想问下,是关于 GC 的。

对于游戏逻辑计算的效率之类的,我可以自己想办法简化,优化,但是对于 GC 时候的卡顿问题,就没什么好办法了。

然后官方也提到了 :

1.6 中升级了 spidermonkey 解决了 GC 卡顿的问题

所以想了解一下,有没有可能在不升级 SpiderMonkey 的前提下,通过对引擎代码做一些修改,来改善 GC 卡顿的问题。换句话说,在这次官方 JSB2.0 的升级中,有没有什么思路是可以参考的,或者说,有没有部分 pull 是可以使用的。

希望官方可以解答一下,谢谢了 :slightly_smiling:

这个主要是SpiderMonkey本身升级解决的。至于别的是否有可能有改善部分可以 @dumganhar

感谢回复,那我想再问一下,如果说是

SpiderMonkey本身升级解决的

那么,如果我只是进行

  1. spidermonkey 的库的升级,
    2, 升级带来的,涉及到 scriptingcore 的一些必要修改
    3, 升级带来的,涉及到的绑定层的一些必要修改
  2. 保留绝大部分的绑定层的代码不变

这种做法

  1. 是不是可行
  2. 是不是也能对 GC 的问题有所缓解

你完全可以把很重的东西放在C++里,JS里跑业务逻辑 好像没有什么慢的情况出现。
我也是用COCOS2DX -JSB

主要是绑定代码的修改比较麻烦。

感谢回复,我已经花了两天的时间,手动把 Creator 1.6.2 的 v52 升级对应到了 cocos2dx 3.12 引擎了。

大部分的功能已经对应, mac,Android 的编译运行也都通过了,目前在手动处理 cocostudio 的绑定部分。

后续也许还有什么别的问题,再来咨询官方 :slightly_smiling:

你这叫升级?这是降级啊。。。

额?为什么这么说呢? 我是把 cocos2d-x 3.12 的 SpiderMonkey v33 升级到 v52 为什么说是降级呢?

我理解为,你手动把creator1.6里面的SpiderMonkey v52"升级"到cocos2dx3.12里面的SpiderMonkey v33的版本。

今天继续测试 v52 的升级,期间碰到了几个问题,希望官方可以解惑一下。

  1. 关于 CC_ENABLE_GC_FOR_NATIVE_OBJECTS

在 cocos2d-x 3.1x 中,这个宏默认都是 0,注释也写的是:

By default this behavior is disabled by default

我下载了 Cocos Creator 1.6.2 beta.3,创建了一个 HelloWorld 工程,发现项目中的这个宏,默认是 1,也就是默认是启用的。

然后我试了下把这个宏关掉,再编译,发现会报错,原因是在这个宏不启用的情况下,ScriptingCore 中还残留这 SpiderMonkey v33 的一些函数的调用,如 JS::RemoveObjectRoot , JS::AddNamedObjectRoot

是不是说,在新的 SpiderMonkey v52 环境下,官方已经强制用户使用 CC_ENABLE_GC_FOR_NATIVE_OBJECTS 这种模式了 ?

  1. jsb_RefFinalizeHook_finalize 崩溃问题 (这个最头痛)

这个可能是 cocostudio 绑定造成的问题 。

今天把我 cocostudio 部分的 auto manual 绑定给对应了一下,然后把之前游戏的 ui (json) 文件拿来做了下测试,结果在 jsb_RefFinalizeHook_finalize 的地方会发生崩溃,原因是:

int count = refObj->getReferenceCount();

这里的 count 已经是 0 了,导致,导致后面的 Ref::release 里面,断言失败

CCASSERT(_referenceCount > 0, "reference count should be greater than 0");

对于这个问题,我做了些调查,现在能提供一些信息:

a. jsb_RefFinalizeHook_finalize 触发的时候,正常情况下,refObj 的类型是 cocos2d::Node*, cocos2d::SpriteFrame* 等等。
但是出错的,refObj 的类型显示是:

b. 每次出现这个问题时候,都是因为解析一个 cocostudio ui 的 mainmenu.json 文件造成的,错误堆栈每次都一样 :

c. 在加载这个 mainmenu.json 界面之前,我已经成功加载和显示了一个 login.json 界面 (包含输入框,按钮之类的)。

d. 如果我修改一下 jsb_RefFinalizeHook_finalize 的代码,把 count == 0 的情况先放过:

int count = refObj->getReferenceCount();
if (count == 1)
{
    refObj->autorelease();
}
else if (count == 0) {
    // 放过
}
else
{
    CC_SAFE_RELEASE(refObj);
}

这样的话,界面可以正常的加载显示,并且上面的按钮之类的也可以正常的使用。

P大下周一来解答你吧,帮你强力@panda一下。

感谢,我周末也会再多做一些调查尝试的。

是的

你遇到的 jsb_RefFinalizeHook_finalize 问题触发的时候已经有对象处在非正常状态了,JS 对象仍然存在的情况下其 C++ 引用计数为 0,这个调查没什么特别好的办法,需要在 preprocess macro 中设置 DEBUG_MODE 为 2,这样会打印更多的信息,可以在出错的时候检查 refObj 的指针,然后在历史 log 中搜索这个指针,看看之前是什么类型的对象,再继续研究为什么会发生问题

感谢回复。

上面提的问题已经解决,是一个 jsb_create_weak_jsobject 造成的问题。

经过上一周的测试,这次 v52 合并到 cocos2d-x 3.12 的尝试应该说是成功了,最大的优点就是安卓上的 jsb 计算能力得到了大幅的提升,已经更好的 GC 管理模式。

下次有时间的话,准备把 CocosCreator 1.7 的 JSB 2.0 给合并到 cocos2d-x 3.1x 里面,性能应该还会有更大的提升。

2赞

最近在使用中,发现了一个内存释放的问题,怎么都找不出原因,想要所以想要咨询一下官方 @panda

首先简单概括一下问题,在新的内存模型下,使用一张单独的图片创建 Sprite 后,无法回收释放图片。

下面是重现的步骤和一些说明(我的理解,可能有错的地方):

  1. 创建一个 cc.Sprite
this.sprite = new cc.Sprite("some.png");

这里先创建一个 Sprite 对象。
绑定层会 创建一个 native ref 对象,引用计数为1;并为其设置 jsb_RefFinalizeHook 在 GC 的时候会触发 jsb_RefFinalizeHook_finalize

  1. 添加到场景
this.addChild(this.sprite); // this == layer

这里的 this 是一个普通的 cc.Layer
这句话,native 会做两件事情
a. native 的 ref 引用计数 +1 ,目前为 2。
b. native 会反过来调用 js 中的 registerNativeRef 函数,把 sprite 和 layer 建立引用关系,防止被 GC
相当于 layer.__nativeRefs[0] <==> sprite

  1. 删除 Sprite (不在同一帧进行)
this.sprite.removeFromParent();

这里也会做两件事:
a. native 会调用 releaseScriptObject ,解除 layer.__nativeRefs[0] <==> sprite 的引用关系
b. 引用计数 -1,目前为 1,还不能析构

  1. 手动 GC ( (不在同一帧进行))
ScriptingCore::forceGC();

这里,应该会触发 sprite 的 jsb_RefFinalizeHook_finalize ,在这里,spriteautorelease

  1. 下一个 mainloop,sprite 被析构

  2. 回收 texture

cc.director.purgeCachedData();

这里,removing unused texture some.png 会显示为被回收。

这个是我认为应该发生的正确流程,但是实际上,却不是这么顺利,这个步骤的 4 会发生问题: 无论手动 GC 多少次,sprite 的 jsb_RefFinalizeHook_finalize 也不会被调用。

除非我在第3步,removeFromParent 之后添加:

this.sprite = null;

那么在一下次的 GC 时,sprite jsb_RefFinalizeHook_finalize 才会触发。

这里我的疑问是,如果我有很多这种 引用对象的形式,那是不是说我必须在想要删除回收资源之前,一个一个的将其设置为 null ?这个显然不太合理。

我也尝试了另一种做法:

  1. 不调用 removeFromParent,而是调用(模仿 Scene::cleanup 的做法)
ScriptingCore::releaseAllChildrenRecursive

这样做,在 GC 的时候,sprite jsb_RefFinalizeHook_finalize 也会触发,但是这时 sprite 的引用计数是2,还不会被autorelease。而再次调用 GC,sprite jsb_RefFinalizeHook_finalize 就再也不会触发了,因此 sprite 一直是保持计数1 的状态,无法析构。

希望官方解惑一下,是不是我的理解或是哪里的做法出了偏差,谢谢了 :slightly_smiling:

你描述的过程基本都是正确的,只是你不理解为什么不会触发 this.sprite 的回收。

这个其实很简单,既然 this.sprite 的引用还存在,就说明从 JS 层仍然可以访问到这个 JS 对象,那么它当然就不是垃圾,自然也不会被 GC 回收。

this.sprite = null; 之后,你之前创建的 JS 对象自然就访问不到了,因为没有任何索引了,那么 GC 就会把它当作垃圾回收掉,也就会触发 jsb_RefFinalizeHook_finalize 回调。

其实新内存模型的意义就在于此,只要 JS 对象不被回收,native 对象的引用计数就无法到达 0,无法被释放,这样保证了 JS 和 native 生命周期的一致性。

厉害 我眼馋creator很久了 无奈没本事自己把cocos2d-js升级过去.

这个内存释放很好理解, jsb中存在c++内存释放 与 js内存释放, 而CC_ENABLE_GC_FOR_NATIVE_OBJECTS就是将两者关联起来 js释放掉后才会释放c++对象. 也就是说你只需要保证js对象能够被内存回收就可以了, 而不是一定要 this.sprite = null;

js内存回收机制有两种, 引用计数与标记清除, 在你上面 =号 就是引用计数.
标记清除自己百度,总之就是你只需要要保证从js的global根对象无法访问到对象就可以了,例:
this.a = new Sprite();
this.a.b = new Sprite();
this.a = null;
这时候并不需要this.a.b =null
更多细节 暂不讨论

OVER

http://forum.cocos.com/t/topic/53075 panda有空看下这个问题么