Unity中的GC優(yōu)化

Unity 的 Mono 引擎等運(yùn)行時(shí)系統(tǒng)會(huì)自動(dòng)管理內(nèi)存端仰,不僅書(shū)寫(xiě)方便而且大大降低了內(nèi)存泄漏的可能性(即分配了內(nèi)存但后續(xù)從未釋放的情況),不過(guò)要使用得當(dāng)跌前,否則會(huì)導(dǎo)致不必要的頻繁觸發(fā)垃圾回收器并在執(zhí)行中引起暫停钮糖。

優(yōu)化案例1:重復(fù)的字符串連接

string str = "";
for (int i = 0; i < 10000; i++)
{
  str += "," + i;
}
Debug.Log(str);

上面這段代碼看上去沒(méi)問(wèn)題,但可能成為垃圾回收的噩夢(mèng)枢析。每次循環(huán)時(shí)玉掸,str 變量的先前內(nèi)容變?yōu)樗劳鰻顟B(tài):分配的整個(gè)新字符串將包含原始部分加上末尾的新部分。由于字符串隨著 i 值的增加而變長(zhǎng)醒叁,因此消耗的堆空間量也會(huì)增加司浪,所以每次調(diào)用此函數(shù)時(shí)都很容易用掉數(shù)百個(gè)字節(jié)的空閑堆空間。如果需要將大量字符串連接在一起把沼,StringBuilder 是更好的選擇啊易。上面的算法可以優(yōu)化為:

StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
  stringBuilder.Append(",");
  stringBuilder.Append(i);
}

Debug.Log(stringBuilder.ToString());

我們可以借助Unity的Profiler對(duì)比結(jié)果:發(fā)現(xiàn)GC減少了一半。

優(yōu)化案例2:字符串頻繁連接

有些小伙伴喜歡在Unity的Update函數(shù)中處理邏輯饮睬,比如:

void Update() {
        string scoreText = "Score: " + score.ToString();
        scoreBoard.text = scoreText;
    }

這樣寫(xiě)在每次調(diào)用 Update 時(shí)都會(huì)分配新的字符串租谈,并生成源源不斷的垃圾⌒妫可以優(yōu)化為僅在score值發(fā)生變化的時(shí)候更新 text垦垂。

優(yōu)化案例3:函數(shù)返回?cái)?shù)組值

public class ExampleScript : MonoBehaviour {
    float[] RandomList(int numElements) {
        var result = new float[numElements];
        
        for (int i = 0; i < numElements; i++) {
            result[i] = Random.value;
        }
        
        return result;
    }
}

在新建包含值的數(shù)組時(shí)宦搬,這種類型的函數(shù)非常從容和方便。但是劫拗,如果重復(fù)調(diào)用這種函數(shù)间校,則每次都會(huì)分配全新的內(nèi)存。由于數(shù)組可能非常大页慷,因此空閑堆空間可能會(huì)迅速耗盡憔足,導(dǎo)致頻繁進(jìn)行垃圾收集。避免此問(wèn)題的一種方法是利用數(shù)組為引用類型這一特點(diǎn)酒繁。作為參數(shù)傳入該函數(shù)的數(shù)組可在該函數(shù)內(nèi)予以修改滓彰,且結(jié)果在函數(shù)返回后仍然保留。像上面這樣的函數(shù)通持萏唬可替換為如下所示的函數(shù):

public class ExampleScript : MonoBehaviour {
    void RandomList(float[] arrayToFill) {
        for (int i = 0; i < arrayToFill.Length; i++) {
            arrayToFill[i] = Random.value;
        }
    }
}

優(yōu)化案例4:對(duì)象的重復(fù)使用

在許多情況下揭绑,通過(guò)減少創(chuàng)建和銷毀的對(duì)象數(shù)量即可避免生成垃圾。游戲中存在某些類型的對(duì)象郎哭,例如飛彈他匪,TableView的Cell等,這些對(duì)象可能會(huì)多次反復(fù)遇到夸研,但是只有少數(shù)對(duì)象會(huì)同時(shí)處于游戲中邦蜜。在這種情況下,通澈ブ粒可以重用對(duì)象悼沈,而不是銷毀舊對(duì)象并替換為新對(duì)象。

優(yōu)化案例5:減少Debug.Log的使用

移除游戲中的Debug.Log()函數(shù)的代碼姐扮,盡管該函數(shù)可能輸出為空絮供,對(duì)該函數(shù)的調(diào)用依然會(huì)執(zhí)行,該函數(shù)會(huì)創(chuàng)建至少一個(gè)字符(空字符)的字符串溶握。如果游戲中有大量的該函數(shù)的調(diào)用杯缺,這會(huì)造成內(nèi)存垃圾的增加。在游戲上線時(shí)我們可以禁用日志:

Debug.unityLogger.logEnabled = false;

優(yōu)化案例6:Unity函數(shù)調(diào)用

調(diào)用GameObject.name 或者 GameObject.tag也會(huì)造成預(yù)想不到的堆內(nèi)存分配睡榆,這兩個(gè)函數(shù)都會(huì)將結(jié)果存為新的字符串返回萍肆,這就會(huì)造成不必要的內(nèi)存垃圾,對(duì)結(jié)果進(jìn)行緩存是一種有效的辦法胀屿,但是在Unity中都對(duì)應(yīng)的有相關(guān)的函數(shù)來(lái)替代塘揣。對(duì)于比較gameObject的tag,可以采用GameObject.CompareTag()來(lái)替代宿崭。

bool isPlayer = other.gameObject.CompareTag(playerTag);

不只是GameObject.CompareTag亲铡,Unity中許多其他的函數(shù)也可以避免內(nèi)存垃圾的生成。比如我們可以用Input.GetTouch()和Input.touchCount()來(lái)代替Input.touches,或者用Physics.SphereCastNonAlloc()來(lái)代替Physics.SphereCastAll()奖蔓。

優(yōu)化案例7:裝箱操作

裝箱操作是指一個(gè)值類型變量被用作引用類型變量時(shí)候的內(nèi)部變換過(guò)程赞草,如果我們向帶有對(duì)象類型參數(shù)的函數(shù)傳入值類型,這就會(huì)觸發(fā)裝箱操作吆鹤。比如String.Format()函數(shù)需要傳入字符串和對(duì)象類型參數(shù)厨疙,如果傳入字符串和int類型數(shù)據(jù),就會(huì)觸發(fā)裝箱操作疑务。如下面代碼所示:

void ExampleFunction()
{
    int cost = 5;
    string displayString = String.Format("Price:{0} gold",cost);
}

在Unity的裝箱操作中沾凄,對(duì)于值類型會(huì)在堆內(nèi)存上分配一個(gè)System.Object類型的引用來(lái)封裝該值類型變量,其對(duì)應(yīng)的緩存就會(huì)產(chǎn)生內(nèi)存垃圾知允。裝箱操作是非常普遍的一種產(chǎn)生內(nèi)存垃圾的行為撒蟀,即使代碼中沒(méi)有直接的對(duì)變量進(jìn)行裝箱操作,在插件或者其他的函數(shù)中也有可能會(huì)產(chǎn)生温鸽。最好的解決辦法是盡可能避免或者移除造成裝箱操作的代碼保屯。

優(yōu)化案例8:主動(dòng)GC

Unity允許開(kāi)發(fā)者禁用垃圾回收和請(qǐng)求垃圾回收,不過(guò)都需要根據(jù)情況謹(jǐn)慎處理涤垫。
//請(qǐng)求垃圾回收

System.GC.Collect();

優(yōu)化案例9:增量垃圾收集

增量式垃圾收集將垃圾收集過(guò)程分散到多個(gè)幀中配椭。
增量式垃圾收集是 Unity 使用的默認(rèn)垃圾收集方法。Unity 仍然使用 Boehm–Demers–Weiser 垃圾收集器雹姊,但是以增量模式運(yùn)行。Unity 不會(huì)在每次運(yùn)行時(shí)進(jìn)行完整的垃圾收集衡楞,而是將垃圾收集工作負(fù)載拆分到多個(gè)幀中吱雏。這意味著,不必單次長(zhǎng)時(shí)間中斷程序的執(zhí)行來(lái)讓垃圾收集器完成工作瘾境,Unity 會(huì)進(jìn)行多次短時(shí)間的中斷歧杏。雖然這不能整體上加快垃圾收集速度,但將工作負(fù)載分布到多個(gè)幀可以極大減少垃圾收集“尖峰”破壞應(yīng)用程序流暢性的問(wèn)題迷守。

環(huán)境:
Unity:2020.3.12f

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末犬绒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子兑凿,更是在濱河造成了極大的恐慌凯力,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礼华,死亡現(xiàn)場(chǎng)離奇詭異咐鹤,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)圣絮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)祈惶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事捧请》采” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵疹蛉,是天一觀的道長(zhǎng)活箕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)氧吐,這世上最難降的妖魔是什么讹蘑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮筑舅,結(jié)果婚禮上座慰,老公的妹妹穿的比我還像新娘。我一直安慰自己翠拣,他們只是感情好版仔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著误墓,像睡著了一般蛮粮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谜慌,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天然想,我揣著相機(jī)與錄音,去河邊找鬼欣范。 笑死变泄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恼琼。 我是一名探鬼主播妨蛹,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拂蝎,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颤难,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體已维,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乐严,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衣摩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昂验。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捂敌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出既琴,到底是詐尸還是另有隱情占婉,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布甫恩,位于F島的核電站逆济,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏磺箕。R本人自食惡果不足惜奖慌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望松靡。 院中可真熱鬧简僧,春花似錦、人聲如沸雕欺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屠列。三九已至啦逆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間笛洛,已是汗流浹背夏志。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苛让,地道東北人盲镶。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蝌诡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枫吧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容