Unity 内存分析
文章目录
介绍
Unity 使用 C# 时会出现内存泄露问题,如何排查 C# 导致的内存泄露是一件很麻烦的事情。
C# 中的各种变量引用、delegate 委托、静态变量等等会通过各种直接或间接的方式引用到资源,会导致垃圾回收机制无法正确回收资源。
环境
- macOS 10.14.6
- Unity 2018.4.25f1
- Unity Memory Profiler 0.2.6-preview-1
- Mumu 1.9.23(20200730)
- Scripting Runtime Version: .Net 4.x Equivalent
- Api Compatibility Level: .Net 4.x
Unity 内置 Profiler
不能使用 Unity 内置的 Profiler,虽然可以同时抓取编辑器与真机的数据,但是无法比较数据。也就意味着难以在大量数据中找到差异,快速定位问题。
必须使用真机测试才能发现问题,不能使用编辑器自身,因为分析工具会影响编辑器。如果使用 Unity 2020.2 中的独立于编辑器运行的 Profiler 应该可以。
Unity Memory Profiler
Unity 官方单独开发了一个专用的内存分析工具,现在处于预览状态。
此工具虽然支持快照功能,但是对于资源之间的引用显示并不是很合理,导致难以追踪不同对象间是如何引用在一起的,实际使用时基本只是当作辅助工具确认使用。
安装
可以在 Package Manager 中直接安装:
复制结果
因为需要将 UI 中的结果以文本形式复制到剪贴板中,方便后续查找,因此尝试输出 UI 选中行以下行的信息到 Console 中。
修改 OnGUI_CellMouseDown 方法代码
com.unity.memoryprofiler@0.2.6-preview.1/Editor/UI/DatabaseSpreadsheet.cs
|
|
Unity Memory Profiler 修改版
有很多修改版的 Unity Memory Profiler
这个版本是基于较老的版本修改的,UI 显示与现在的 0.2.6 完全不同;其次最后提交时间是 2019/06,已经过去一年半了,没有生命力。
- Unity MemoryProfiler 的工作机制及可能的改进 - 知乎
- GitHub - GameBuildingBlocks/PerfAssist: Various performance-related components for Unity development.
这个版本也很旧,提升了差异项与提升序列化速度。
开发了UnityProfiler和MemoryCrawler两款工具,分别替代Profiler以及MemoryProfiler进行相同领域的性能调试,它们均使用纯C++实现,因为经过与C#、Python语言的测试对比后发现C++有绝对的计算优势,可以非常明显提升性能数据分析效率和稳定性。
Heap Explorer
找到一个非常合适的工具,GitHub 上的只支持 Unity 2019.3,BitBucket 上版本支持 Unity 2017.4 - 2019.2,
源码版本导入后有编译报错:
'AbstractTreeView.CanMultiSelect(TreeViewItem)': no suitable method found to override
DLL 版本没有编译报错,但是打开窗口时提示
The MemoryProfiling API does not work with .NET 4.x Scripting runtime, which your project makes use of.
实测 DLL 版本可用,可以直接无视上面的警告。
DLL 版本下载地址
其他链接
- GitHub - pschraut/UnityHeapExplorer: A Memory Profiler, Debugger and Analyzer for Unity 2019.3 and newer.
- pschraut / unityheapexplorer — Bitbucket
- Heap Explorer - Memory Profiler, Debugger and Analyzer for Unity - YouTube
- HeapExplorer.pdf 旧版本的文档 PDF 提供了相当详细的信息,建议阅读
分析过程
模拟器
使用 Mumu 模拟器启动程序后,需要转发端口,然后在 Memory Profiler 中连接 127.0.0.1 adb forward tcp:55000 localabstract:Unity-com.jjyou.ydxj.traceless 55000
手机
需要打开开发者模式并允许 USB 调试
Unity
Unity | Window | Analysis | Profiler
点击 Editor 按钮,在弹出的下拉菜单中选择 127.0.0.1,就可以连接到手机或模拟器上。
分析方法
引用
不同项目会有不同类型的引用,要提前分类整理好,方便后续处理:
- Unity 引用:通过 GUID 引用另一个资源,例如场景中保存其他 Prefab,Prefab 中引用其他 Prefab
- C# 文本引用:通过表格数据中保存的 UI 路径字符串,在 UI 管理器动态加载
- C# 静态引用:通过静态类对资源产生的引用,Unity 内置 Profiler 与 Memory Profiler 都无法查找,只是显示为 ManagedStaticReferences(),Heap Explorer 可以显示这种引用
- Lua 文本引用:通过在 UI Prefab 中的字段中设置另一个 UI 的路径,在运行时通过 UI 管理器加载
- Lua 文本引用:在 Lua 代码中指定要打开的界面路径,使用 C# 接口打开 UI
- Lua 静态引用:通过 C# 接口加载完成资源后存放在 Lua 变量中
不光要查引用,还要查引用为什么没有在正确的时间被断开!这涉及到仔细分析代码,了解对象的生命周期,确定资源释放的时机。
分析问题
使用比较快照功能按类型分组、按大小排序,先处理纹理,一般来说纹理都是内存泄露的大头,按照 28 原则优先处理。
结合上面提到的各种引用方式,分析资源是如何被引用的,又是为何为断开引用释放。