Mono源碼閱讀-GC造成內(nèi)存泄露問題

本文主要記錄Mono源碼中會(huì)因?yàn)镚C的問題,造成Unity游戲不可避免的都會(huì)存在一定得內(nèi)存泄露問題的底層原因耕驰,涉及到Mono源碼中GC機(jī)制的邏輯据德。

前提條件:

要出現(xiàn)這種內(nèi)存泄露肩民,必須先準(zhǔn)備一塊任意的內(nèi)存塊:(無任何外部引用般此,理論上應(yīng)該會(huì)在用完后被GC蚪战,但在該BUG下會(huì)錯(cuò)誤的泄露,不被GC掉)

byte[] buffer = new byte[2097168];  // 內(nèi)存起始地址:0xbe82f000铐懊,內(nèi)存結(jié)束地址:0xde468c50 內(nèi)存占用大醒!:2 MB

NOTE:大小任意,越大越容易被泄露科乎。

一塊struct結(jié)構(gòu)的數(shù)組:(struct內(nèi)必須有一個(gè)類似指針的值類型, 如int壁畸,和另一個(gè)引用類型,如string)

struct Slot {
    int hashCode;
    String value;
}
Slot[] slots = new Slot[5000];   // 內(nèi)存起始地址:0xde45f000茅茂, 內(nèi)存結(jié)束地址:0xde468c50捏萍, 內(nèi)存占用大小:40016 B,

NOTE:大小任意空闲,數(shù)組內(nèi)的元素越多越容易觸發(fā)泄露令杈。例如 HashSet<String> 內(nèi)部使用了該數(shù)據(jù)格式。

通過在GC中打點(diǎn)碴倾,和使用GDB調(diào)用GC過程这揣,以便觀察所有對(duì)象的分配和GC的過程發(fā)現(xiàn):buffer對(duì)象錯(cuò)誤的被slots對(duì)象引用,導(dǎo)致buffer對(duì)象無法被正常GC影斑,造成內(nèi)存泄露。

原因分析:

首先對(duì)于mono/il2cpp的Boehm GC庫而言机打, mono/il2cpp的對(duì)象在分配內(nèi)存的時(shí)候矫户,會(huì)有幾種類型:

  • NORMAL:無類型的內(nèi)存分配,對(duì)于GC而言残邀,因?yàn)闊o法得到對(duì)象的類型元數(shù)據(jù)皆辽,所以在做GC時(shí)會(huì)按指針對(duì)齊的方式掃描該內(nèi)存塊柑蛇,只要發(fā)現(xiàn)類似通過指針校驗(yàn)的地址都會(huì)認(rèn)為該對(duì)象引用了該指針地址指向的對(duì)象。
  • PTRFREE:無指針內(nèi)存分配驱闷,明確告知GC耻台,該對(duì)象內(nèi)無任何指針信息,即在GC時(shí)無需查找該對(duì)象內(nèi)是否引用其他對(duì)象空另。在mono/il2cpp中的int型的數(shù)組盆耽,byte型的數(shù)組,字符串等使用該類型的內(nèi)存分配扼菠。
  • TYPED: 有類型的內(nèi)存分配摄杂,讓GC知曉該對(duì)象的內(nèi)存布局,在做GC時(shí)無需盲目的掃描內(nèi)存查找引用的指針循榆,而直接按照對(duì)象的內(nèi)存布局查找指針地址析恢,建立對(duì)象和對(duì)象之間的引用關(guān)系,在mono/il2cpp中的class類型的對(duì)象是使用該類型的內(nèi)存分配秧饮。

而在本例中:slots的分配是用NORMAL類型映挂,buffer對(duì)象的分配是用PTRFREE類型。

因此在做GC的時(shí)候盗尸,對(duì)于slots對(duì)象柑船,GC會(huì)掃描該對(duì)象的內(nèi)存區(qū)間,查找其內(nèi)部的指針地址振劳,即從0xde45f000到0xde468c50地址按照指針對(duì)齊的方式查找指針地址:
例如:0xde45f000 0xde464d44 0xde464f40 0xde46513c ....
其中出現(xiàn)了 0xde464f40 這個(gè)地址的值剛好為:0xbe82f000(即Slot結(jié)構(gòu)體內(nèi)hashCode的值)椎组,而GC會(huì)錯(cuò)誤的將該int型數(shù)值當(dāng)做指針,而該指針剛好又指向了一塊GC托管的內(nèi)存塊历恐,即buffer對(duì)象寸癌,因此GC認(rèn)為該buffer對(duì)象被slots對(duì)象內(nèi)部引用了,buffer對(duì)象也被GC標(biāo)記弱贼,不會(huì)被釋放蒸苇。

4cc08e81ac33f9ddf066b1aa4f2d731.png

該問題的關(guān)鍵在于,GC將slot結(jié)構(gòu)體內(nèi)的hashcode這個(gè)int值錯(cuò)誤的當(dāng)做的指針吮旅,而該int值剛好又指向了另一個(gè)托管的對(duì)象溪烤,因此GC錯(cuò)認(rèn)為了兩個(gè)對(duì)象存在引用關(guān)系,而造成內(nèi)存泄露庇勃。

最小化Demo:

public class NewBehaviourScript : MonoBehaviour {


    struct Slot
    {
        public int hashCode;
        public string value;
    }


    private static Slot[] slots = new Slot[5000];


    void Start () {
        for(var i = 0; i < slots.Length; ++i) {
            slots[i].hashCode = i * (1024 * 1023) + 1;
        }
     }


    void Update () {
        byte[] buffer = new byte[2 * 1024 * 1024]; // memory leak
    }
}

將struct Slot修改為class Slot檬嘀,則可修復(fù)內(nèi)存泄露問題,因?yàn)閏lass對(duì)象的內(nèi)存分配時(shí)TYPED類型责嚷。

因?yàn)镸ono的GC的設(shè)計(jì)問題鸳兽,Unity游戲中幾乎不可避免的都會(huì)隨著時(shí)間出現(xiàn)內(nèi)存泄露問題,因?yàn)槔鏗ashSet這種數(shù)據(jù)結(jié)構(gòu)內(nèi)部都會(huì)出現(xiàn)該問題罕拂。但我們可以做的事情揍异,依然是內(nèi)存使用的兩大真理(特別是虛擬機(jī)類型的語言):

  • 減少頻繁分配小內(nèi)存
  • 減少一次性分配大內(nèi)存

這樣做全陨,不能完全避免Mono的底層GC問題,但是它可以讓這種內(nèi)存泄露的變得更加平緩衷掷。


NOTE ATTRIBUTES

Created Date: 2019-11-12 10:28:06
Last Evernote Update Date: 2020-05-23 07:43:32

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辱姨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子戚嗅,更是在濱河造成了極大的恐慌雨涛,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渡处,死亡現(xiàn)場離奇詭異镜悉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)医瘫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門侣肄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人醇份,你說我怎么就攤上這事稼锅。” “怎么了僚纷?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵矩距,是天一觀的道長。 經(jīng)常有香客問我怖竭,道長锥债,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任痊臭,我火速辦了婚禮哮肚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘广匙。我一直安慰自己允趟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布鸦致。 她就那樣靜靜地躺著潮剪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪分唾。 梳的紋絲不亂的頭發(fā)上抗碰,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音绽乔,去河邊找鬼改含。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捍壤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鞍爱,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼鹃觉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起睹逃,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤盗扇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后沉填,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疗隶,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年翼闹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斑鼻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猎荠,死狀恐怖坚弱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情关摇,我是刑警寧澤荒叶,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站输虱,受9級(jí)特大地震影響些楣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宪睹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一愁茁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧横堡,春花似錦埋市、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胸蛛,卻和暖如春污茵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背葬项。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工泞当, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人民珍。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓襟士,卻偏偏與公主長得像盗飒,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陋桂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355