Windows下Unity内存泄露排查

背景

项目使用Unity开发,使用过程中可以明显内存有异常增长,所以排查了一下原因,做一下记录。

分三步描述

  1. 寻找异常内存分配时的地址
  2. 寻找异常内存分配的堆栈
  3. 寻找对应的项目代码

寻找异常内存分配时的地址

后台用的工具

使用massif可以详细打印内存分配时的堆栈信息,但是没有Windows版本,询问GPT后得到了下面几个工具。

  1. Unity自带的Profile
  2. vs2022
  3. vmmap64

工具一,Unity自带的Profile

拍摄两个快照,发现内存异常增长的部分被划分到了Unknown,也不会显示堆栈所以需要换工具。

工具二,vs2022

Attach到Unity之后使用堆快照无法发现明显的内存增长,换工具。

工具三vmmap64

附加到Unity进程之后会自动拍摄快照,之后隔一段时间手动F5刷新一下。之后点击Timeline,选择对应的时间范围,可以看到这期间的内存新增申请。可以明显看到不是Heap在增长,而是Private Data在增长, 且都是相同的524284K,符合内存泄露的特征。

所以下一步需要找到不断申请的524284K的Private Data是谁申请的。

观察一下分配的虚拟内存是524284K,但这并不是实际占用的物理内存,最终占用的物理内存是258000K左右(另一部分保留了)。可以理解为这一块内存是逐渐从0K占用逐渐增长到了258000K。

每次F5刷新时,可以看到新的524284K大小的虚拟内存申请和对应的起始地址如0xA0000,之后Unity会顺序写入直到0xA0000+258000K。

了解到这一点之后,可以使用硬件断点设置写内存断点,当向指定地址写入时触发断点,进而查看堆栈。

所以需要一个内存地址,从上文可以看到内存是逐渐从0xA0000到0xA0000+258000K,所以拍摄快照时,如果内存使用到了0xA0000+1000K,则其必定会访问到0xA0000+10000K,所以对0xA0000+10000K下断点等待Unity的访问。

寻找异常内存分配的堆栈

Windbg

Attach到Unity进程,之后恢复Unity运行,返回vmmap64拍摄快照之后,立刻返回Windbg将Unity暂停。

回到vmmap64,查看最新的524284K的起始地址和已经使用大小,计算一个将来会访问的地址0xA123456。

回到windbg使用ba w 4 0xA123456 设置硬件断点,之后恢复Unity的运行,等到Unity访问到524284K内存块中的0xA123456时就会触发断点。此时就获得了向这块异常内存块写入数据时的堆栈。

寻找对应的项目代码

查看Unity源码

找到对应的相关代码,是有开关的功能,查找开关的开启和关闭的接口。回到项目中找使用接口的代码,找到问题代码。

最终是因为开始Profile后没有关闭,导致Unity的Profile一直运行,内存一直增长。

存在问题

vmmap64中显示这块内存分配到了PrivateData, vs2022的堆快照无法发现问题, 暂不清楚Unity是如何使用内存的。