备注:这是三年前还在做游戏项目时分享的一篇文章,虽然已经过去三年,但其中所涉及的方法却一直都在使用,因此再贴出来,希望能帮到一部分程序工程师朋友。
在项目的整个开发过程中一直伴随着手机客户端的内存问题,时不时的也会出现一些内存导致的Crash情况,出现Crash的原因可能会有很多,但在IOS设备上很多常常是由于内存吃紧导致的,如果出现内存不够用而Crash的一个直观表现是在Iphone 6 plus设备上更容易重现,这是由于6P的内存只有1G,但由于该设备屏幕更大而需要更多的Frame buffer空间,因此在该IOS 设备的内存最为吃紧。
很多内存问题在PC的开发阶段就可以暴露出现,这种情况比较容易处理,毕竟PC上工具链很丰富,本文要探讨的问题是在IOS设备上导致的一些问题,本文也介绍工具是Xcode的Instruments提供的Memory Leaks工具,如图,由下图中可以看出Instruments提供了很多工具可供开发使用,其它工具慢慢摸索。
image如何启动Leaks工具,参考官方文档[自行搜索吧,头条不允许放外链]
发现问题:
在Xcode中启动游戏后,使用内存监控工具查看内存的变化情况,发现当场景Load完成后一段时间内内存是稳定的,然后过段时间内存突然涨了赶来,如下图所示。出现这个问题第一个反应是去检查了一下代码逻辑,打出了一些Log后也没有发现有哪里会突然需要分配这么多的内存,纠结了一会之后突然想来Xcode提供的Memory Profile工具,于是乎有了后面的内容。
image选择游戏项目,使用Leaks工具启动Record功能,在游戏内跑一下游戏,得到了如下的内存使用时间序列图,首先从图中可以看出Neox引擎在启动游戏不久之后就出现了内存泄漏(图中红色的叉),占进去看一下吧^^,所幸这些Leaks并非是导致内存暴涨的原因。
image image粗略看了一上内存泄漏的部分,可以看出这些漏泄并不是本次关心的重点,重点是最内存使用图中最后一次的增长,那么这一时间段内到底是什么占用了内存呢?所幸Instruments给我们提供了查看某一时间段内内存分配的功能,好了,现在就我们把锁定到第40s左右来看看。
image如图中蓝色部分,时间 大概是39s-42s之间,这段时间内内存增加了大约70M, Allocation Summary部分给出了这段时间内内存的分配情况。从图中我们可以看到有一个68M的内存分配,那么这些内存是分配给谁了呢?带着这个问题,我们一层一层的点进去看看究竟吧。
image进去之后,我们对分配内存的操作进行排下序,会发现有10次的内存分配操作,每次分配了6.6M的内存。也即是说这段时间内NXMemoryFile分配了66M的内存。还好在这一步我们还可以继续往下深入分析,最终定位到分配内存的那段代码:
image至此,可以说是找到了内存暴涨的主要原因的线索,由于NXMemoryFile是一个基础的模块,其实我们还没有找到究竟是谁调用了这段代码,其实到了这一步问题就变得很简单了,加个断点,根据call stack来查找问题源头。ps, 由于Xcode出了点问题,执行断点时加载不出来了调用栈,这里面就不贴图了,栈的源头是_audio.cpp模块加载fsb文件。为什么fsb文件的加载会导致内存的暴涨呢?
FMOD模块在加载fsb时,以流的方式读取解压文件,所以播放FSB格式的音频文件理论上来说不会导致太多的内存增长,而问题是我们将fsb文件打入了NPK文件内部,因为Fmod模块没有办法直接使用流的方式读取npk文件,因此为了能够加载npk内的fsb文件,neox里面有一个基于内存的文件系统NXMemoryFile,先将文件读取到内存中,然后fmod才能以文件的形式加载fsb数据,因此也就导致了内存的暴涨。
另外(由于没看Fmod代码,这是根据程序运行时的表现猜测的)Fmod在读fsb时应该是open一个文件,然后持有一个handler,而每一个handler对应一个内存文件,这也就导致了同一份fsb文件在内存中会有多个内存文件。
既然定位到问题是由于将FSB文件打入npk导致的,那么就尝试将fsb文件从npk文件中分离出来,下图是不再将fsb打入npk的客户端的运行情况,内存的使用情况较先前有了很大的好转,同时在不同设备上运行一段时间之后,crash的情况也确实变得很少了。
image
网友评论