備注:這是三年前還在做游戲項目時分享的一篇文章,雖然已經(jīng)過去三年,但其中所涉及的方法卻一直都在使用,因此再貼出來,希望能幫到一部分程序工程師朋友。
在項目的整個開發(fā)過程中一直伴隨著手機客戶端的內(nèi)存問題,時不時的也會出現(xiàn)一些內(nèi)存導(dǎo)致的Crash情況,出現(xiàn)Crash的原因可能會有很多,但在IOS設(shè)備上很多常常是由于內(nèi)存吃緊導(dǎo)致的,如果出現(xiàn)內(nèi)存不夠用而Crash的一個直觀表現(xiàn)是在Iphone 6 plus設(shè)備上更容易重現(xiàn),這是由于6P的內(nèi)存只有1G,但由于該設(shè)備屏幕更大而需要更多的Frame buffer空間,因此在該IOS 設(shè)備的內(nèi)存最為吃緊。
很多內(nèi)存問題在PC的開發(fā)階段就可以暴露出現(xiàn),這種情況比較容易處理,畢竟PC上工具鏈很豐富,本文要探討的問題是在IOS設(shè)備上導(dǎo)致的一些問題,本文也介紹工具是Xcode的Instruments提供的Memory Leaks工具,如圖,由下圖中可以看出Instruments提供了很多工具可供開發(fā)使用,其它工具慢慢摸索。
如何啟動Leaks工具,參考官方文檔[自行搜索吧,頭條不允許放外鏈]
發(fā)現(xiàn)問題:
在Xcode中啟動游戲后,使用內(nèi)存監(jiān)控工具查看內(nèi)存的變化情況,發(fā)現(xiàn)當(dāng)場景Load完成后一段時間內(nèi)內(nèi)存是穩(wěn)定的,然后過段時間內(nèi)存突然漲了趕來,如下圖所示。出現(xiàn)這個問題第一個反應(yīng)是去檢查了一下代碼邏輯,打出了一些Log后也沒有發(fā)現(xiàn)有哪里會突然需要分配這么多的內(nèi)存,糾結(jié)了一會之后突然想來Xcode提供的Memory Profile工具,于是乎有了后面的內(nèi)容。
選擇游戲項目,使用Leaks工具啟動Record功能,在游戲內(nèi)跑一下游戲,得到了如下的內(nèi)存使用時間序列圖,首先從圖中可以看出Neox引擎在啟動游戲不久之后就出現(xiàn)了內(nèi)存泄漏(圖中紅色的叉),占進去看一下吧^^,所幸這些Leaks并非是導(dǎo)致內(nèi)存暴漲的原因。
粗略看了一上內(nèi)存泄漏的部分,可以看出這些漏泄并不是本次關(guān)心的重點,重點是最內(nèi)存使用圖中最后一次的增長,那么這一時間段內(nèi)到底是什么占用了內(nèi)存呢?所幸Instruments給我們提供了查看某一時間段內(nèi)內(nèi)存分配的功能,好了,現(xiàn)在就我們把鎖定到第40s左右來看看。
如圖中藍色部分,時間 大概是39s-42s之間,這段時間內(nèi)內(nèi)存增加了大約70M, Allocation Summary部分給出了這段時間內(nèi)內(nèi)存的分配情況。從圖中我們可以看到有一個68M的內(nèi)存分配,那么這些內(nèi)存是分配給誰了呢?帶著這個問題,我們一層一層的點進去看看究竟吧。
進去之后,我們對分配內(nèi)存的操作進行排下序,會發(fā)現(xiàn)有10次的內(nèi)存分配操作,每次分配了6.6M的內(nèi)存。也即是說這段時間內(nèi)NXMemoryFile分配了66M的內(nèi)存。還好在這一步我們還可以繼續(xù)往下深入分析,最終定位到分配內(nèi)存的那段代碼:
至此,可以說是找到了內(nèi)存暴漲的主要原因的線索,由于NXMemoryFile是一個基礎(chǔ)的模塊,其實我們還沒有找到究竟是誰調(diào)用了這段代碼,其實到了這一步問題就變得很簡單了,加個斷點,根據(jù)call stack來查找問題源頭。ps, 由于Xcode出了點問題,執(zhí)行斷點時加載不出來了調(diào)用棧,這里面就不貼圖了,棧的源頭是_audio.cpp模塊加載fsb文件。為什么fsb文件的加載會導(dǎo)致內(nèi)存的暴漲呢?
FMOD模塊在加載fsb時,以流的方式讀取解壓文件,所以播放FSB格式的音頻文件理論上來說不會導(dǎo)致太多的內(nèi)存增長,而問題是我們將fsb文件打入了NPK文件內(nèi)部,因為Fmod模塊沒有辦法直接使用流的方式讀取npk文件,因此為了能夠加載npk內(nèi)的fsb文件,neox里面有一個基于內(nèi)存的文件系統(tǒng)NXMemoryFile,先將文件讀取到內(nèi)存中,然后fmod才能以文件的形式加載fsb數(shù)據(jù),因此也就導(dǎo)致了內(nèi)存的暴漲。
另外(由于沒看Fmod代碼,這是根據(jù)程序運行時的表現(xiàn)猜測的)Fmod在讀fsb時應(yīng)該是open一個文件,然后持有一個handler,而每一個handler對應(yīng)一個內(nèi)存文件,這也就導(dǎo)致了同一份fsb文件在內(nèi)存中會有多個內(nèi)存文件。
既然定位到問題是由于將FSB文件打入npk導(dǎo)致的,那么就嘗試將fsb文件從npk文件中分離出來,下圖是不再將fsb打入npk的客戶端的運行情況,內(nèi)存的使用情況較先前有了很大的好轉(zhuǎn),同時在不同設(shè)備上運行一段時間之后,crash的情況也確實變得很少了。