如何让手游内存占用更小?从内存消耗iOS实时统计开始

 

为什么iOS内存使用过多会崩溃,性能会下降?腾讯游戏学院专家Devlin在本文给了解释,如何让手游内存占用更小?从内存消耗iOS实时统计开始。

一、问题

在之前的手游项目中,内存使用过多,都开始崩溃了,所以得做iOS内存统计。内存统计有好几种方法:XCode内存使用统计、UnityInternalProfile内存统计,Mono内存统计等方法。

但是XCode统计需要连手机,UnityInternalProfile的内存统计值与XCode内存统计值差距又太大,崩溃时的内存值跟谁有关系?如何在手机上自己显示内存总量?后面就自己琢磨怎样实现一个适合的内存统计功能。

二、测试研究

研究了下UnityInternalProfile,发现它拿的是mach_base_task_info里的resident_size(物理内存占用)如何让手游内存占用更小?从内存消耗iOS实时统计开始

然后我做了个测试,每几帧分配使用一定大小的内存,然后打印出xcode统计的内存和resident_size。

如何让手游内存占用更小?从内存消耗iOS实时统计开始

横坐标是时间,纵坐标是内存。

resident_size值的增长随着内存增长,但增长到一定程度就不怎么变了,当时猜测可能是被压缩了,查资料(MacOS有使用内存压缩技术)和代码,发现iOS还有task_vm_info 这个结构体,里面刚好有compress这项。

如何让手游内存占用更小?从内存消耗iOS实时统计开始

然后增加compress这项数值的输出,重新测试!

如何让手游内存占用更小?从内存消耗iOS实时统计开始

在内存使用持续增加过程中,当resident_size(物理内存)不再增加时,compress这项线性增长。

三、推论

由图可以看出:实际内存使用 = resident + compress。由此可以认为iOS通过压缩内存来减少内存占用。

并且在测试过程中,发现当实际使用内存达到系统物理内存一半时,系统会不断发送memorywarning的警告,达到60%时就会Q掉App。

四、应用

现在只需要实时拿到task_vm_info里的resident 和 compress 就可以统计App的实际内存的使用量了,对于Unity手机项目来说,需要写Native和C#代码,幸运的是,我已经帮你把代码写好了。

在XCodePostProcess::OnPostProcessBuild()里加入如下代码,会在Unity生成的XCode工程自动插入如下Native代码:

XClass AppRender = new XClass(pathToBuiltProject + "/Classes/UnityAppController+Rendering.mm");
if( AppRender != null)
{
string TCode = "";
TCode += "#include <mach/mach_time.h>\n";
TCode += "#include <mach/mach.h>\n";
TCode += "#include <mach/mach_host.h>\n";
TCode += "#include <mach/task_info.h>\n";
TCode += "#include <mach/task.h>\n";
TCode += "static float GetTotalPhysicsMemory( )\n";
TCode += "{\n";
TCode += " kern_return_t kr;\n";
TCode += " mach_msg_type_number_t info_count = TASK_VM_INFO_COUNT;\n";
TCode += " task_vm_info_data_t vm_info;\n";
TCode += " kr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vm_info, &info_count);\n";
TCode += " if (kr == KERN_SUCCESS) return (float)(vm_info.compressed + vm_info.resident_size) / 1024.0 / 1024.0;\n";
TCode += " return 0;\n";
TCode += "}\n";
TCode += "extern \"C\" float _Get_Profiler_TotalPhysicMemory(){return _fLockStepPhysicMemory;}\n";
TCode += "extern \"C\" void UnityRepaint()";

AppRender.Replace("extern \"C\" void UnityRepaint()",TCode );
}
(左右滑动可查看全部代码)

在UnityC#里加入以下托管代码,调用 Get_Profiler_TotalPhysicMemory()即可实时拿到内存使用值。

#if ( UNITY_IPHONE && !UNITY_EDITOR )

[DllImport("__Internal")]
static extern float _Get_Profiler_TotalPhysicMemory( );

public static float Get_Profiler_TotalPhysicMemory( )
{
return _Get_Profiler_TotalPhysicMemory( );
}
#endif
(左右滑动可查看全部代码)

五、补充

由于系统有分页机制,即你申请使用1字节的内存,系统也有可能会给你一整页(16k大小的物理页),所以会导致这里的实际内存使用量(内存分页总和)与XCode内存统计(精确统计)不完成相等,但大致符合一定比例。

上一篇:SAP知道后台路径如何查找后台配置的TCODE(后台配置的事务码)


下一篇:[转帖]SAP进阶:再论SAP权限