unity 微信小程序iOS内存优化与管控经验

原文链接

https://blog.csdn.net/weixin_40137140/article/details/134083407?spm=1001.2014.3001.5501

前言

本篇文章是作者本人通过查看微信unity小程序官方内存优化文档和结合自身项目经验总结所得,会有一定的重复内容,在观看本篇文章之前,请仔细浏览微信官方内存优化文档,否则可能对文章无法理解,如有错误,欢迎指出。

微信unity小程序优化文档:https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/OptimizationMemory.md

一.unity小程序内存限制

1.轻度游戏:

  • 进程内存 < 1gb
  • UnityHeap < 300MB

    2.中重度游戏:

    • 进程内存 < 1.4gb
    • UnityHeap < 450MB

      二.unity小程序内存分布

      用作者知道的一个中重度游戏(优化一般)做分析:

      1.进程内存详情分析:

      • UnityHeap占比只有33%,可操作空间不是特别大
      • 内存浮动主要是大量特效显示,浏览器无法及时释放内存
      • 显存大小并不精准,作者推测所得,实际应该在100~200M之间。
      • WASM编译占比较大,原因是代码和依赖库太多。

        2.UnityHeap内存:

        • 缓存空间是用于存储资源对象池,防止资源频繁加载释放,实际资源+缓存空间占比大约在50%。
        • Lua内存和托管堆较大。

          3.通过以上信息得出结论:

          • 在进程层面,可以对代码包体,渲染效果,内存浮动方面进行排查优化。
          • 在unity业务层,我们可以对资源,Lua和托管堆进行优化。

            三.监控工具

            1. unity profiler监听,真机连profiler才准确,编辑器不准确。

              目前微信连真机profiler的教程。

              工具地址:https://q1doc.yuque.com/kiob3t/gynxh4/eb4qe7

            2. lua profiler插件工具地址:https://github.com/leinlin/Miku-LuaProfiler
            3. unity 微信转换插件的Profiling Memory 内存分析,具体操作信息看微信内存优化官方文档。
            1. 正式包上通过接口获取内存,观察变化,如LuaMemory,NativeReserverd,Total Used Memory
            2. 进程级别: Perfdog、Android Studio、 Mac Xcode Instrument

            三.进程内存

            1. 内存分类(照搬微信文档):
            • 基础库+Canvas:在小游戏环境中并不存在DOM,但依然会存在一些基本消耗,比如小游戏公共库,Canvas画布等。典型地,小游戏公共库约占用内存100~150MB,Canvas 画布与设备物理分辨率相关,比如iPhone 11 Promax占用约80MB。

            • Unity Heap: 托管堆、本机堆与原生插件底层内存。举例,游戏逻辑分配的C#对象等托管内存、Unity管理的AssetBundle和场景结构等本机内存、第三方原生插件(如lua)调用的malloc分配。

            • WASM编译: 代码编译与运行时指令优化产生的内存,在Android v8、iOS JavascriptCore中还需要大量内存进行JIT优化

            • GPU内存:纹理或模型Upload GPU之后的显存占用, 由于Unity2021之前不支持压缩纹理,纹理内存会造成明显膨胀。

            • 音频:Unity将音频传递给容器(浏览器或小游戏)后,播放音频时将占用的内存。目前UnityAudio将自动适配微信小游戏,特别地请避免使用fmod播放长音频。

            • 内存浮动:主要是渲染压力大时,浏览器缓存没有及时释放。

              1. 获取手段(单位MB):
              • Unity Heap:微信转换插件的Profiling Memory查看
              • GPU内存:目前作者是通过进程总内存减去其他内存得出
              • WASM编译: 内存大小≈wasm未压缩文件大小*10(注意是未分包情况)
              • 内存浮动:可以使用perfDog或者Instrument测试真机情况下进程最大内存峰值时(特效多的时候)调用wx.triggerGC,查看进程内存下降额度,多测几次就大致得出结论。接口文档:https://developers.weixin.qq.com/minigame/dev/api/base/performance/wx.triggerGC.html
                1. 优化手段:
                • GPU内存:主要是显示效果,RenderTexture或者抓屏,shader效果之类入手。
                • WASM编译: 开启unity代码剔除(裁剪)功能,使用微信分包工具**(中重度游戏必须处理)**,

                  XLua Warp生成加上 GEN_CODE_MINIMIZE 宏,减少Warp生成数量,降低代码包大小,微信代码分包地址:https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/WasmSplit.md

                • 内存浮动:内存浮动较大的项目可以通过定时调用wx.triggerGC来防止内存突破限制导致崩溃。
                • 音频:使用微信音频

                  四.UnityHeap内存

                  1. 内存分类:
                  • TotalHeapMemory: UnityHeap总预分配内存大小
                  • DynamicMemory:UnityHeap当前使用上限,受MonoHeap与NativeReserverd 等影响,崩溃直接表现值,只会增长不会下降。
                  • NativeReserverd:本机堆(Native)内存分配峰值,集中在unity底层自动分配的内存(主要是资源)。
                  • MonoHeap: 托管堆当前使用内存,主要是游戏中各种对象相关的实例内存,如C#数组,List之类堆内存,峰值只增不减,峰值就代表当前内存。
                  • LuaMemory: Lua内存,属于第三方插件malloc产生。
                    1. 底层分配器:

                    • 绿色为空闲内存或碎片,底层分配器会尽量复用
                    • 白色为预留部分,可被使用
                    • 其他颜色,已被业务使用
                      1. 获取手段(单位MB):
                      • DynamicMemory ≈ MonoHeap + NativeReserverd + 原生插件内存+30,结果是三个加起来峰值最大的时候
                      • NativeReserverd = Profiler.GetTotalReservedMemoryLong()*0.00000095367431
                      • MonoHeap = Profiler.GetMonoUsedSizeLong()*0.00000095367431
                      • LuaMemory = LuaAPI.lua_gc(L, LuaGCOptions.LUA_GCCOUNT, 0)*0.0009765625
                        1. 优化手段:
                        • DynamicMemory: 使用定时器,每帧通过上面获取手段的计算方式得出当前DynamicMemory的使用大小,然后决定业务层是否要调用内存释放相关操作,防止Dynamic峰值上升。
                        • NativeReserverd:通过资源优化,AB卸载资源释放,Resources.UnloadUnusedAssets操作等,下降峰值。
                        • MonoHeap:管控峰值,避免某个操作导致内存急速增长,但是过后又释放,会让托管堆在长时间使用率低,峰值高的情况,导致内存浪费,需要注意的是在WebGL 托管堆主动调用GC无效。
                        • 排查lua内存泄露,错误逻辑(循环溢出)导致的内存溢出或者抓着引用不释放。
                          1. 排查手段:
                          • 使用UnityProfiler和Unity MemoryProfiler检查托管堆与本机堆内存详情。
                          • 使用LuaProfiler查看Lua内存详情,排查内存异常增长与内存泄露情况。

                            五.微信平台导致内存上升的操作(一定要避免!!!)

                            1. UnityWebRequest(主要是AB资源的Cache)的 下载缓存接口在WebGL平台上会导致内存爆发式增长,缓存的所有文件都会算进运行内存。文档地址:https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/OptimizationMemory.md

                            如下列带Cache的接口:

                            1. IO操作,主要集中在后台下载并发过高,文件写入频繁(如日志写入,下载记录写入等操作)导致的内存溢出之类,可以实施的操作有:
                            • 增加文件操作句柄接口,防止需要连续写入的文本文件调用WXFileSystemManager.WriteFile频繁创建销毁IO流,速度过慢导致写入数据累积。接口地址:https://developers.weixin.qq.com/miniprogram/dev/api/file/FileSystemManager.open.html
                            • 写入间隔不能太频繁,写入数据量不宜过大。
                            • 下载文件也会有IO操作,如果是后台下载之类可以调整并发数。

                              六.其他一些内存优化方向

                              1. 微信小游戏纹理压缩,文档地址:https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/CompressedTexture.md
                              2. 使用Unity Profiler在真机上排查大资源和废弃资源。
                              3. 使用AssetStudio排查大资源,废弃资源和重复资源。重复资源排查工具地址:https://blog.csdn.net/weixin_40137140/article/details/134082859?spm=1001.2014.3001.5501
                              1. 增加设置像素比接口,iOS上降低真机像素比,可以有效下降内存,一般设成2就行。
                              1. 表格数据去重,在小程序上因为内存比较紧张,某些类型的游戏往往表格数据较多,处理不当内存可能会突破100M,无论是存储在C#还是lua上,都应该对数据进行合并,尤其是大量的字符串,具体算法可以结合自身项目发挥。

                              2. unity 图片资源格式关闭Read/Write与Mipmap