org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 崩溃分析

已阅读相关贴

Google Play后台崩溃日志 - Creator - Cocos中文社区

cocos creator 2.0.6 版本在安卓中退出后再重新进入游戏时崩溃 - Creator - Cocos中文社区

我们项目(单机未使用热更新)从bugly上捕获了一批Android 的NDK崩溃,崩溃次数并不多,堆栈PC地址分析后的调用栈

v8::HandleScope::Initialize(v8::Isolate*)
:?

AutoHandleScope
js-bindings/jswrapper/v8/ScriptEngine.hpp:65

se::ScriptEngine::init()
js-bindings/jswrapper/v8/ScriptEngine.cpp:379

se::ScriptEngine::start()
js-bindings/jswrapper/v8/ScriptEngine.cpp:570

AppDelegate::applicationDidFinishLaunching()
AppDelegate.cpp:65

Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit
android/jni/JniImp.cpp:197

明显这是引擎启动发生了crash,简单分析多条记录后发现,触发crash的用户使用时长从1分钟到数小时不等。
也就是说,这是第二次调用初始化。

在App未关闭,什么情况会导致二次调用引擎初始化,以下是我对这个问题的分析

Android的Activity的生命周期内,如弹出一个前台AdActivity,则CocosActivity进入后台,发生内存不足时,系统就会清理后台Activity,对应的GLsurfaceview也自然跟着被删除,GLSurfaceView清理后,系统也会自动处理GLSurfaceView对应的EGLContext内存,如opengl里面的各种BufferData、Texture、framebuffer等资源都会一并清理, 但这个清理不会去清除NDK/JNI部分! 引擎原生c++部分的内存和V8依然还在。

AdActivity关闭退回游戏Act,CocosActivity重建,这时执行过程就是上面的crash堆栈表。

由于se::ScriptEngine是单例,在第一次的CocosActivity创建时就已经被创建,所以二次Act重建不会创建新的ScriptEngine实例,但这里涉及到了v8的一个知识点。

使用v8创建可执行的js runtime有以下过程,创建v8::platform-> 创建v8::Isolate -> 创建v8::Context -> 执行jsb注册-> 运行js

一次App进程内只需创建一次 v8::platform即可, 但v8::Isolate不同,这个对象和线程是绑定的,不同线程不能混用v8::Isolate,使用就崩(可以新建一个thread测试),创建了本线程的Isolate,则后续js 运行环境以及jsb绑定才能可用,AutoHandleScope才能正常调用。

基于上述知识点,我们完全可以在一个cocos程序里面创建2个完全独立不干扰的js runtime环境(本人亲测)

android 上cocos主进程和v8的主进程是靠GlSurfaceView内部的GLThread驱动。
,这意味着,不同的GLSurefaceView不能共享:Isolate,也就是不能共用 ScriptEngine实例。

问题解决思路,在CocosActivity被销毁(不管是主动退出还是系统清理)后,都该主动去destroy ScriptEngine实例,这样二次重建可以保证一定重新创建新的 Isolate。

客观说,这个问题不好重现,毕竟没有方法主动去触发后台Act清理,所以建议引擎开发人员加上GLSurefaceView的销毁后续处理机制,总感觉怎么改都会内存泄漏

有人有相关方面的研究结论,欢迎一起讨论

1赞

我用2.4.6版本做的app也出现这种情况了,线上触发概率极高,自己又复现不出来,不知道怎么修复,太痛苦了。

这个时候需要艾特大佬

比较难复现,也不太好解决

说到底,引擎做好了启动进入,但没做好退出销毁,官方默认是跟app进程绑定(不求同年同月生 … 的那种)

我目前思路是,发生Activity onDestroy,就把原生的 AppDeletegate 删除
AppDeletegate 继承与cocos2d::Application, 其析构函数有删除js引擎的操作 ,但引擎的删除不太彻底,如Scheduler、ThreadPool、AutoReleasePool 这些可能还需要自己处理,

AppDeletegate 的创建在 main.cpp

具体怎么写呢,不太了解这方面的知识。在Cocos2dxActivity.java的onDestroy()里写吗?

同样线上也有大量这种 crash。

这种最好还是由官方处理好些 @jare

3.x有没有解决?2.4.6还有这个问题

2.x 引擎中的 Cocos2dxActivity 的 onDestroy 方法会直接把进程给杀死,的确没有处理资源的释放流程。

你是注释掉杀进程,然后发送消息给 native 去销毁 AppDelegate 么?

        Log.d(TAG, "Cocos2dxActivity onDestroy: " + this + ", mGLSurfaceView" + mGLSurfaceView);
        if (mGLSurfaceView != null) {
            Cocos2dxHelper.terminateProcess();
        }

ScriptEngine 是单件,它的构造和析构会对 v8 platform 做初始化和释放的操作,v8 platform 的初始化只能在一个进程初始化一次,所以释放 ScriptEngine 的时候不应该去调用 ScriptEngine::destroyInstance,而应该直接调用 ScriptEngine::getInstance()->cleanup()