Android 11 - native C++ 库的 System.loadLibrary 需要 60 多秒,在 Android 10 及更低版本上运行速度非常快

标签 android c++ java-native-interface cocos2d-x android-11

在我们基于游戏引擎 cocos2d-x 的 Android 游戏应用程序中,大部分代码都是用 C++ 编写的,自 Android 11 以来我们有一个非常奇怪和关键的问题:
当 native 库在 onLoadNativeLibraries 中加载时现在突然需要 60 多秒。在 Android 11 之前,一切正常,加载时间为 0.2-3 秒。现在,当您开始游戏时,您会看到 60 多秒的灰屏。
我们已经知道 JNI_OnLoad在 60 秒的停顿结束后直接调用。
这是 onLoadNativeLibraries 的代码
功能:

protected void onLoadNativeLibraries()
{
    try
    {
        ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
        Bundle bundle = ai.metaData;
        String libName = bundle.getString("android.app.lib_name");
        System.loadLibrary(libName); // line of 60 seconds stall
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
我们已经尝试过时间分析,但没有任何成功。它只是如何在该功能上花费大量时间。通过调试暂停也不会导致任何进一步的线索。 native 调试器不会在代码的 C++ 端显示任何内容。
有人知道为什么会发生这种情况,或者我们可以尝试解决什么问题吗?任何帮助将不胜感激:)

最佳答案

简短回答:
这是谷歌修复的 Android 11 中的一个错误,但尚未部署。
同时,如果您不关心在程序退出/库卸载时调用库中的静态和 thread_local 变量析构函数,请传递标志 -fno-c++-static-destructors给编译器。 (有关使用 clang 注释的更细粒度的解决方案,请参阅长答案)
我在我的项目(不是 cocos2d)上使用了这个标志,没有问题,并且 lib 的加载速度比以前更快。
长答案:
不幸的是,这是 Google 团队在 android 11 (R) 中引入的性能回归。 google 正在跟踪该问题 here .
总而言之,当 System.loadLibrary()被调用时,系统为加载的库中包含的每个 C++ 全局变量注册一个析构函数,使用 __cxa_atexit()
从Android 11(R)开始,这个函数在android中的实现有changed :

  • In Q, __cxa_atexit uses a linked list of chunks, and calls mprotect twice on the single chunk to be modified.
  • In R, __cxa_atexit calls mprotect twice on a single contiguous array of handlers. Each array entry is 2 pointers.

当它们是许多 C++ 全局变量时,这种变化大大降低了性能,这在 cocos2d so 库中似乎就是这种情况。
Google 已经实现了修复 https://android-review.googlesource.com/c/platform/bionic/+/1464716
如问题所述:

this won't be in Android 11 until the March QPR at the earliest, and since this isn't a security issue it won't be mandatory for OEMs to actually take that patch.


谷歌团队还建议一些 workarounds在应用程序级别通过删除或跳过全局变量的析构函数:
  • For a particular global variable, the [[clang::no_destroy]] attribute skips the destructor call.
  • Pass -fno-c++-static-destructors to the compiler to skip the destructors for all static variables. This flag also skips destructors for thread_local variables. If there are thread_local variables with important destructors, those can be annotated with [[clang::always_destroy]] to override the compiler flag.
  • Pass -Wexit-time-destructors to the compiler to make it warn on every instance of an exit-time destructor, to highlight where the __cxa_atexit registrations are coming from.

关于Android 11 - native C++ 库的 System.loadLibrary 需要 60 多秒,在 Android 10 及更低版本上运行速度非常快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64576236/

相关文章:

javascript - Cookie 未存储在 Android Webview 中

java - Firebase - 无法添加多个子对象

c++ - C 链接和 C++ 头文件

c++ - 为什么这个变量在函数调用后会变得困惑

android - C++库导致 native Android崩溃

java - Java中卸载dll

android - 如何在 webview 中显示无互联网连接对话框 |安卓工作室

java - getSupportFragmentManager() 在我的适配器类中不起作用?

c++ - GLFW 设置回调函数

android - 停止/取消执行 FFmpeg 命令