Mono GC

什么是Mono內(nèi)存
對于目前絕大多數(shù)基于Unity引擎開發(fā)的項目而言,其托管堆內(nèi)存是由Mono分配和管理的《湎模“托管” 的本意是Mono可以自動地改變堆的大小來適應你所需要的內(nèi)存良价,并且適時地調(diào)用垃圾回收(Garbage Collection)操作來釋放已經(jīng)不需要的內(nèi)存,從而降低開發(fā)人員在代碼內(nèi)存管理方面的門檻搏色。Unity游戲在運行時的內(nèi)存占用情況可以用下圖表示:


game.jpg

目前絕大部分Unity游戲邏輯代碼所使用的語言為C#善茎,C#代碼所占用的內(nèi)存又稱為mono內(nèi)存,這是因為Unity是通過mono來跨平臺解析并運行C#代碼的频轿,在Android系統(tǒng)上垂涯,游戲的lib目錄下存在的libmono.so文件,就是mono在Android系統(tǒng)上的實現(xiàn)航邢。C#代碼通過mono解析執(zhí)行耕赘,所需要的內(nèi)存自然也是由mono來進行分配管理,下面就介紹一下mono的內(nèi)存管理策略以及內(nèi)存泄漏分析膳殷。

Mono內(nèi)存管理策略

Mono通過垃圾回收機制(Garbage Collect操骡,簡稱GC)對內(nèi)存進行管理。Mono內(nèi)存分為兩部分赚窃,已用內(nèi)存(used)和堆內(nèi)存(heap)册招,已用內(nèi)存指的是mono實際需要使用的內(nèi)存,堆內(nèi)存指的是mono向操作系統(tǒng)申請的內(nèi)存勒极,兩者的差值就是mono的空閑內(nèi)存是掰。當mono需要分配內(nèi)存時,會先查看空閑內(nèi)存是否足夠辱匿,如果足夠的話冀惭,直接在空閑內(nèi)存中分配,否則mono會進行一次GC以釋放更多的空閑內(nèi)存掀鹅,如果GC之后仍然沒有足夠的空閑內(nèi)存散休,則mono會向操作系統(tǒng)申請內(nèi)存,并擴充堆內(nèi)存乐尊,具體如下圖所示戚丸。


fenpei.jpg

通過上文可知,GC的主要作用在于從已用內(nèi)存中找出那些不再需要使用的內(nèi)存扔嵌,并進行釋放限府。Mono中的GC主要有以下幾個步驟:

1.停止所有需要mono內(nèi)存分配的線程。

2.遍歷所有已用內(nèi)存痢缎,找到那些不再需要使用的內(nèi)存胁勺,并進行標記。

3.釋放被標記的內(nèi)存到空閑內(nèi)存独旷。

4.重新開始被停止的線程署穗。

除了空閑內(nèi)存不足時mono會自動調(diào)用GC外寥裂,也可以在代碼中調(diào)用GC.Collect()手動進行GC,但是案疲,GC本身是比較耗時的操作封恰,而且由于GC會暫停那些需要mono內(nèi)存分配的線程(C#代碼創(chuàng)建的線程和主線程),因此無論是否在主線程中調(diào)用褐啡,GC都會導致游戲一定程度的卡頓诺舔,需要謹慎處理。另外备畦,GC釋放的內(nèi)存只會留給mono使用低飒,并不會交還給操作系統(tǒng),因此mono堆內(nèi)存是只增不減的懂盐。

Mono內(nèi)存泄漏分析

Mono是如何判斷已用內(nèi)存中哪些是不再需要使用的呢逸嘀?是通過引用關系的方式來進行的。Mono會跟蹤每次內(nèi)存分配的動作允粤,并維護一個分配對象表崭倘,當GC的時候,以全局數(shù)據(jù)區(qū)和當前寄存器中的對象為根節(jié)點类垫,按照引用關系進行遍歷司光,對于遍歷到的每一個對象,將其標記為活的(alive)悉患。

image.png

如上圖所示残家,假設A是處于全局數(shù)據(jù)區(qū)的一個對象,那么在GC的時候?qū)⒆鳛楦?jié)點進行遍歷售躁,由于B坞淮、C、D對象都可以由A遍歷到陪捷,因此被標記為活的回窘,E、F對象則沒有被標記市袖。注意啡直,由于引用關系是單向的,A引用了B并不代表B也引用了A苍碟,所以遍歷也只能單向進行酒觅。

由于GC以全局數(shù)據(jù)區(qū)和當前寄存器中的對象為根節(jié)點進行遍歷,所以對象的被標記意味著該對象可以通過全局對象或者當前上下文訪問到微峰,而沒有被標記的對象則意味著該對象無法通過任何途徑訪問到舷丹,即該對象“失聯(lián)”了,GC最終會將所有“失聯(lián)”的對象內(nèi)存進行回收蜓肆,上圖中的E和F將會在GC過程中被回收颜凯。

既然mono已經(jīng)有了完善的GC機制谋币,那是否還會存在內(nèi)存泄漏呢?答案是肯定的装获,只是此處的內(nèi)存泄漏需要重新定義一下瑞信,我們把對象已經(jīng)不再需要使用卻沒有被GC回收的情況稱為mono內(nèi)存泄漏厉颤。Mono內(nèi)存泄漏會使空閑內(nèi)存減少穴豫,GC頻繁,mono堆不斷擴充逼友,最終導致游戲內(nèi)存占用的升高精肃。下圖就是一個mono內(nèi)存泄漏的例子。


res.jpg

解決辦法

對于mono內(nèi)存泄漏帜乞,一般只能通過猜測+不斷修改代碼測試的方法來修復問題司抱,效率很低,騰訊Wetest平臺的Cube工具提供了mono內(nèi)存快照對比的功能黎烈,并包括對象分配堆棧习柠,對象引用關系等詳細信息,是定位mono內(nèi)存泄漏問題的一大利器照棋。下面結(jié)合具體的代碼嘗試使用Cube定位mono內(nèi)存泄漏問題资溃。首先我們定義類A,并在A的構(gòu)造函數(shù)中申請了一塊int[1000]大小的內(nèi)存烈炭。

image.png

接著我們定義A類型的靜態(tài)變量objectA溶锭,在游戲界面上繪制一個按鈕,并在按鈕點擊事件中給objectA賦值符隙,此時新生成了new int[1000]對象趴捅,并由objectA引用。


image.png

使用Cube的mono內(nèi)存檢測功能霹疫,并在按鈕按下之前和按下之后分別進行一次快照拱绑,對比兩次快照,查看快照間新增對象丽蝎。


snapshot.jpg

可以看到欺栗,按鈕按下前后新增的最大對象即為代碼中生成的new int[1000]對象,并且該對象被引用的次數(shù)為1征峦,為了查看詳細的引用關系迟几,下載快照文件snapshot2,其中有這樣兩行數(shù)據(jù):
image.png

第一行說明在OnGUI函數(shù)中生成了一個A類型的對象栏笆,其指針為1533098928类腮,第二行說明在OnGUI()->A:.cotr()中生成了一個Int32[]類型的對象,并且該對象被指針為1533098928的對象引用蛉加。即new int[1000]對象被objectA引用蚜枢,這也是導致new int[1000]對象無法被GC回收的原因缸逃。而objectA本身是一個靜態(tài)對象,是GC的根節(jié)點厂抽,因此沒有對象引用需频。
如果需要生成的new int[1000]對象被回收怎么做呢?很簡單筷凤,將objectA.a設置為null昭殉,沒有了objectA對其的引用,自然會被GC回收了藐守。需要說明的是挪丢,將objectA.a設置為null只是斷絕了引用關系,真正對象的回收要等到GC的時候才會進行卢厂,Cube在獲取內(nèi)存快照的時候會首先進行一次GC乾蓬,防止由于沒有及時調(diào)用GC導致的誤判。

游戲中大部分mono內(nèi)存泄漏的情況都是由于靜態(tài)對象的引用引起的慎恒,因此對于靜態(tài)對象的使用需要特別注意任内,盡量少用靜態(tài)對象,對于不再需要的對象將其引用設置為null融柬,使其可以被GC及時回收死嗦,但是由于游戲代碼過于復雜,對象間的引用關系層層嵌套丹鸿,真正操作起來難度很大越走。可以首先使用Cube工具進行分析靠欢,根據(jù)mono內(nèi)存趨勢找出泄漏的具體場景廊敌,然后再使用快照對比功能進行詳細分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末门怪,一起剝皮案震驚了整個濱河市骡澈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掷空,老刑警劉巖肋殴,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坦弟,居然都是意外死亡护锤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門酿傍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烙懦,“玉大人,你說我怎么就攤上這事赤炒÷任觯” “怎么了亏较?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掩缓。 經(jīng)常有香客問我雪情,道長,這世上最難降的妖魔是什么你辣? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任巡通,我火速辦了婚禮,結(jié)果婚禮上绢记,老公的妹妹穿的比我還像新娘扁达。我一直安慰自己正卧,他們只是感情好蠢熄,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著炉旷,像睡著了一般签孔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窘行,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天饥追,我揣著相機與錄音,去河邊找鬼罐盔。 笑死但绕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的惶看。 我是一名探鬼主播捏顺,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼纬黎!你這毒婦竟也來了幅骄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤本今,失蹤者是張志新(化名)和其女友劉穎拆座,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冠息,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡挪凑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逛艰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躏碳。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓮孙,靈堂內(nèi)的尸體忽然破棺而出唐断,到底是詐尸還是另有隱情选脊,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布脸甘,位于F島的核電站恳啥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丹诀。R本人自食惡果不足惜钝的,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铆遭。 院中可真熱鬧硝桩,春花似錦、人聲如沸枚荣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽橄妆。三九已至衙伶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間害碾,已是汗流浹背矢劲。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慌随,地道東北人芬沉。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像阁猜,于是被迫代替她去往敵國和親丸逸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353