備注:這是三年前還在做游戲項(xiàng)目時(shí)分享的一篇文章,雖然已經(jīng)過去三年,但其中所涉及的方法卻一直都在使用扬绪,因此再貼出來,希望能幫到一部分程序工程師朋友裤唠。
在項(xiàng)目的整個(gè)開發(fā)過程中一直伴隨著手機(jī)客戶端的內(nèi)存問題挤牛,時(shí)不時(shí)的也會(huì)出現(xiàn)一些內(nèi)存導(dǎo)致的Crash情況,出現(xiàn)Crash的原因可能會(huì)有很多种蘸,但在IOS設(shè)備上很多常常是由于內(nèi)存吃緊導(dǎo)致的墓赴,如果出現(xiàn)內(nèi)存不夠用而Crash的一個(gè)直觀表現(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ā)使用亭饵,其它工具慢慢摸索休偶。
如何啟動(dòng)Leaks工具,參考官方文檔[自行搜索吧辜羊,頭條不允許放外鏈]
發(fā)現(xiàn)問題:
在Xcode中啟動(dòng)游戲后踏兜,使用內(nèi)存監(jiān)控工具查看內(nèi)存的變化情況,發(fā)現(xiàn)當(dāng)場景Load完成后一段時(shí)間內(nèi)內(nèi)存是穩(wěn)定的只冻,然后過段時(shí)間內(nèi)存突然漲了趕來庇麦,如下圖所示。出現(xiàn)這個(gè)問題第一個(gè)反應(yīng)是去檢查了一下代碼邏輯喜德,打出了一些Log后也沒有發(fā)現(xiàn)有哪里會(huì)突然需要分配這么多的內(nèi)存,糾結(jié)了一會(huì)之后突然想來Xcode提供的Memory Profile工具垮媒,于是乎有了后面的內(nèi)容舍悯。
選擇游戲項(xiàng)目,使用Leaks工具啟動(dòng)Record功能睡雇,在游戲內(nèi)跑一下游戲萌衬,得到了如下的內(nèi)存使用時(shí)間序列圖,首先從圖中可以看出Neox引擎在啟動(dòng)游戲不久之后就出現(xiàn)了內(nèi)存泄漏(圖中紅色的叉)它抱,占進(jìn)去看一下吧^^秕豫,所幸這些Leaks并非是導(dǎo)致內(nèi)存暴漲的原因。
粗略看了一上內(nèi)存泄漏的部分,可以看出這些漏泄并不是本次關(guān)心的重點(diǎn)混移,重點(diǎn)是最內(nèi)存使用圖中最后一次的增長祠墅,那么這一時(shí)間段內(nèi)到底是什么占用了內(nèi)存呢?所幸Instruments給我們提供了查看某一時(shí)間段內(nèi)內(nèi)存分配的功能歌径,好了毁嗦,現(xiàn)在就我們把鎖定到第40s左右來看看。
如圖中藍(lán)色部分回铛,時(shí)間 大概是39s-42s之間狗准,這段時(shí)間內(nèi)內(nèi)存增加了大約70M, Allocation Summary部分給出了這段時(shí)間內(nèi)內(nèi)存的分配情況。從圖中我們可以看到有一個(gè)68M的內(nèi)存分配茵肃,那么這些內(nèi)存是分配給誰了呢腔长?帶著這個(gè)問題,我們一層一層的點(diǎn)進(jìn)去看看究竟吧验残。
進(jìn)去之后捞附,我們對(duì)分配內(nèi)存的操作進(jìn)行排下序,會(huì)發(fā)現(xiàn)有10次的內(nèi)存分配操作胚膊,每次分配了6.6M的內(nèi)存故俐。也即是說這段時(shí)間內(nèi)NXMemoryFile分配了66M的內(nèi)存。還好在這一步我們還可以繼續(xù)往下深入分析紊婉,最終定位到分配內(nèi)存的那段代碼:
至此药版,可以說是找到了內(nèi)存暴漲的主要原因的線索,由于NXMemoryFile是一個(gè)基礎(chǔ)的模塊喻犁,其實(shí)我們還沒有找到究竟是誰調(diào)用了這段代碼槽片,其實(shí)到了這一步問題就變得很簡單了,加個(gè)斷點(diǎn)肢础,根據(jù)call stack來查找問題源頭还栓。ps, 由于Xcode出了點(diǎn)問題,執(zhí)行斷點(diǎn)時(shí)加載不出來了調(diào)用棧传轰,這里面就不貼圖了剩盒,棧的源頭是_audio.cpp模塊加載fsb文件。為什么fsb文件的加載會(huì)導(dǎo)致內(nèi)存的暴漲呢慨蛙?
FMOD模塊在加載fsb時(shí)辽聊,以流的方式讀取解壓文件,所以播放FSB格式的音頻文件理論上來說不會(huì)導(dǎo)致太多的內(nèi)存增長期贫,而問題是我們將fsb文件打入了NPK文件內(nèi)部跟匆,因?yàn)镕mod模塊沒有辦法直接使用流的方式讀取npk文件,因此為了能夠加載npk內(nèi)的fsb文件通砍,neox里面有一個(gè)基于內(nèi)存的文件系統(tǒng)NXMemoryFile玛臂,先將文件讀取到內(nèi)存中,然后fmod才能以文件的形式加載fsb數(shù)據(jù),因此也就導(dǎo)致了內(nèi)存的暴漲迹冤。
另外(由于沒看Fmod代碼讽营,這是根據(jù)程序運(yùn)行時(shí)的表現(xiàn)猜測(cè)的)Fmod在讀fsb時(shí)應(yīng)該是open一個(gè)文件,然后持有一個(gè)handler叁巨,而每一個(gè)handler對(duì)應(yīng)一個(gè)內(nèi)存文件斑匪,這也就導(dǎo)致了同一份fsb文件在內(nèi)存中會(huì)有多個(gè)內(nèi)存文件。
既然定位到問題是由于將FSB文件打入npk導(dǎo)致的锋勺,那么就嘗試將fsb文件從npk文件中分離出來蚀瘸,下圖是不再將fsb打入npk的客戶端的運(yùn)行情況,內(nèi)存的使用情況較先前有了很大的好轉(zhuǎn)庶橱,同時(shí)在不同設(shè)備上運(yùn)行一段時(shí)間之后贮勃,crash的情況也確實(shí)變得很少了。