剧目
| 座椅进程内存超标(1.2GB),3D的还是安卓的? |
|---|
| 性能测试发现座椅进程内存占用超标,测试提bug,安卓工程师分析认为是3D超的,把bug单转给了3D。 |
| |
本故事纯属虚构,仅用来表达业务思考。
角色表
"你这些什么Heap什么Dump的,我哪看得懂?"
"不解耦的下场就是要花时间沟通这种你的、我的、还是他的问题"
对话
张明哲(安卓) :金平,这个内存占用超标的bug我转给你了你看下,紧急bug,今日必解。
金平(3D) :我看到了这个bug,但是我有点看不懂啊,你说的Heap是啥?
张明哲(安卓) :那我先说下,情况是座椅调节界面在进入退出三次后,进程总内存占用达到了1.2GB,比标准值高出500MB。我在Android Studio抓了Heap Dump,你看这个Android Studio的Profiler截图(投屏显示)。Heap Analyzer 中前面几个大的:
android.graphics.Bitmap的实例数不多,但Native Size这一列加起来将近220MB
这些加起来四五百兆,跟你们每次打开界面涨的那一截对得上。

金平(3D) :你说的这些byte[]和Bitmap是我申请的?我Unity这边加载资源用的是引擎自己的API,比如AssetBundle.LoadAssetAsync,走的是引擎自己的资源管线,我代码里根本没有什么android.graphics.Bitmap,也没有BitmapFactory啊。
有点懵,这些不会真是Unity申请的内存吧...
张明哲(安卓) :对象名字是Java的,不代表跟你们无关。你看这个引用链,大块byte[]最终能追到UnityPlayerActivity附近;Bitmap的Native Size又这么大,我安卓这边的素材都不会这么大的,Unity里加载的贴图更有可能吧?

金平(3D) :引用追到UnityPlayerActivity,那也不能说明它是我Unity业务逻辑的Texture吧。再说了,我们Unity的Texture2D、Mesh这些在引擎Native堆和GPU侧,根本不应该会以Texture2D这种类名出现在你这个Heap Analyzer列表里。你拿Java Heap Dump的结果,直接认作Unity的,可能不对哦。
你拿Java的分析工具看Unity的纹理内存,这是不是有点牛头不对马嘴?
张明哲(安卓) :我试过,我去掉 3D 的 Library,然后打包后本地台架测过了,内存是正常的。但是加了你们的 Library 就复现了,切换几次,进程总内存就涨一截。这总归是你们3D这边有什么问题吧。
哪有那么多时间和你一起深入到你自己那块去分析。我就用这个去掉 3D 做对比的招儿,你总没话说吧。
金平(3D) :不可能啊,每次切换,我这里又不会重复加载什么资源,怎么可能打开一次就涨一截内存呢?!你说的“每次涨一截”,涨的是哪一块内存?
张明哲(安卓) :你看这个Allocation Call Stack。这条大块分配落在android.graphics.Bitmap.nativeCreate,上层是BitmapFactory.decodeByteArray,再往上是宿主侧的一张图解码路径,最后间接关联到你们Unity界面打开时的业务回调。

金平(3D) :这条是Android Bitmap解码路径,不是Unity的AssetBundle或是纹理的什么路径。Unity从AssetBundle加载纹理,走的是引擎Native解码和Graphics API,正常不会经过BitmapFactory.decodeByteArray。你这条链路更像是App主线程自己在解码Bitmap,跟我Prefab加载不是同一条管线,你是不是在接我这块的时候,加了什么Bitmap的东西啊?
我没用Bitmap啊,这是哪来的?
张明哲(安卓) :呃……我之前是有做了一个对 3D 的截图的机制。那个时候,因为热启的时候3D座椅画面老是慢半拍恢复,我只能拿截图挡一下,去解那些“闪一下”的问题。Heap Dump里的这一两百兆可能是我这里的问题,我会后再去看看。但是还有呢,还有两三百兆的增量哪来的呢?
我之前以为Native Heap就代表所有Native内存,看来不全是。
好像确实截图有点问题,不全是Unity的,这里怕是有两个问题!
金平(3D) :把完整的dumpsys meminfo 秀出来看一下呢。
难不成 3D 这里是大头所在?
张明哲(安卓) :行,adb shell dumpsys meminfo <pid>
Applications Memory Usage (in Kilobytes):** MEMINFO in pid 28453 [com.example.unityapp] ** Pss Private Private SwapPss Total Dirty Clean Dirty Native Heap 280000 275000 5000 0 Dalvik Heap 45000 44000 1000 0 Dalvik Other 5000 4800 200 0 Stack 512 512 0 0 Ashmem 2 0 2 0 Gfx dev 8500 8500 0 0 Other dev 200 120 80 0 .so mmap 95000 2000 65000 0 .jar mmap 800 0 400 0 .apk mmap 1200 0 600 0 .ttf mmap 300 0 200 0 .dex mmap 3000 2800 200 0 .oat mmap 1500 0 800 0 .art mmap 2000 1500 500 0 Other mmap 500 50 450 0 EGL mtrack 420000 420000 0 0 GL mtrack 380000 380000 0 0 Unknown 30000 29800 200 0 TOTAL 1273514 1171082 74232 0 App Summary Pss(KB) ------ Java Heap: 45000 Native Heap: 280000 Code: 98000 Stack: 512 Graphics: 808500 Private Other: 40000 System: 9502 TOTAL: 1273514 TOTAL PSS
金平(3D) :Graphics应该就是3D渲染相关内存的大头。Unity的纹理、Mesh的GPU缓冲、RenderTexture主要在这里。你之前的Heap Dump 肯定就不是了。
CPU侧:
- Native Heap(~280MB):
malloc/new出来的C/C++堆,含Unity引擎运行时、IL2CPP、部分资源解码缓冲等 - Dalvik/Java Heap(~45MB):宿主Activity、View、以及你Heap Dump里能直接点到的Java对象
- .so mmap(~95MB):
libunity.so、libil2cpp.so等动态库映射
Graphics侧(合计约808MB):
- GL mtrack(~380MB):驱动上报的GL内存,主要是纹理、GL命令缓冲、部分驱动固定开销
- EGL mtrack(~420MB):主要是Surface,EGL分配的buffer
Graphics 这 800MB 里,有多少是座椅界面、有多少是别的界面残留,我得靠Unity Memory Profiler和你的meminfo对比。如果有没卸干净的GPU资源也有可能造成这种现象。
张明哲(安卓) :行吧,那你查你的,我去查我的。
金平(3D) :嗯,切换后如果 Graphics 占用和 Unity 快照都能回落,那问题就不大,就不是内存泄漏,只是需要做内存占用优化,我之后会把结论更新到bug单中的。
哎,逃过一劫。
结语
Unity 以 Library 嵌进 Android 时,它会有:Java 堆(Heap Dump 主要在此)、Native Heap(引擎/运行时及各类 native 分配)、Graphics(GL/EGL mtrack 等图形相关总量)。
Heap Dump 看不出 Unity 纹理/Mesh 的 GPU 明细;内存资源明细还是需要看 Unity Memory Profiler。
还是服务化渲染好,大家不共进程,能少处理些掰不清楚的问题。
对于加了3D就怎么怎么,不加就没问题的说法,要意识到,这也可能是3D的运行,让宿主App的错误逻辑或是系统驱动的潜在问题显现出来了。