如何讓手遊記憶體佔用更小?從記憶體消耗iOS實時統計開始

遊資網發表於2019-12-19
為什麼iOS記憶體使用過多會崩潰,效能會下降?騰訊遊戲學院專家Devlin在本文給瞭解釋,並且給出了統計記憶體的示例。

一,問題

在之前的手遊專案中,記憶體使用過多,都開始崩潰了,所以得做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記憶體統計(精確統計)不完成相等,但大致符合一定比例。

關於騰訊遊戲學院專家團

如果你的遊戲也富有想法充滿創意,如果你的團隊現在也遇到了一些開發瓶頸,那麼歡迎你來聯絡我們。騰訊遊戲學院聚集了騰訊及行業內策劃、美術、程式等領域的遊戲專家,我們將為全世界的創意遊戲團隊提供專業的技術指導和遊戲調優建議,解決團隊在開發過程中遇到的一系列問題。

申請專家資源請前往:

https://gwb.tencent.com/cn/tutor


來源:騰訊GWB遊戲無界
原文:https://mp.weixin.qq.com/s/s4tXl7KnjrDSnftbQSeiDg

相關文章