Unity Mono托管內(nèi)存優(yōu)化

引子

優(yōu)化Mono的托管內(nèi)存
Mono的托管內(nèi)存的優(yōu)化主要是代碼的優(yōu)化小染,以下部分是我在網(wǎng)上收集的資料:

內(nèi)容

1.盡量不要動態(tài)的Instantiate和Destroy Object絮蒿,使用Object Pool杈女。看看我之前寫的一篇文章就基本懂了豁遭!

2.盡量不要再Update函數(shù)中做復(fù)雜計算卧斟,如有需要社搅,可以隔N幀計算一次。

3.不要動態(tài)的產(chǎn)生字符串挚躯,如Debug.Log("boo" + "hoo")强衡,盡量預(yù)先創(chuàng)建好這些字符串資源。

4.Cache一些東西码荔,在update里面盡量避免search漩勤,如GameObject.FindWithTag("")、GetComponent這樣的調(diào)用缩搅,可以在Start中預(yù)先存起來越败。

5.盡量減少函數(shù)調(diào)用棧,用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)

6.下面的代碼是幾個GC“噩夢”誉己。 String的相加操作眉尸,會頻繁申請內(nèi)存并釋放,導(dǎo)致gc頻繁巨双,使用System.Text.StringBuilder代替

void ConcatExample(intArray: int[]) {  
   var line = intArray[0].ToString()噪猾;  
   for (i = 1; i < intArray.Length; i++) {  
                   line += ", " + intArray[i].ToString();  

   }  
   return line;  
}

7.在函數(shù)中動態(tài)new array,最好將一個array筑累、傳進(jìn)函數(shù)里修改

8.Update處理可改為每5幀處理一次:

void Update() { DoSomeThing(); } 
void Update() { if(Time.frameCount % 5 == 0) { DoSomeThing();}}

9.定時重復(fù)處理用 InvokeRepeating 函數(shù)實現(xiàn) 比如袱蜡,啟動0.5秒后每隔1秒執(zhí)行一次 DoSomeThing 函數(shù):

void Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }

10.優(yōu)化 Update, FixedUpdate, LateUpdate 等每幀處理的函數(shù) 函數(shù)里面的變量盡量在頭部聲明。比如:

void Update() { var pos: Vector3 = transform.position; }

可改為

private var pos: Vector3; void Update(){ pos = transform.position; }

11.主動回收垃圾,給某個 GameObject 綁上以下的代碼:

void Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }

12.優(yōu)化數(shù)學(xué)計算,比如:如果可以避免使用浮點型(float)慢宗,盡量使用整形(int)坪蚁,盡量少用復(fù)雜的數(shù)學(xué)函數(shù)比如 Sin 和 Cos 等等奔穿。

13.減少固定增量時間,將固定增量時間值設(shè)定在0.04-0.067區(qū)間(即敏晤,每秒15-25幀)贱田。您可以通過Edit->Project Settings->Time來改變這個值。這樣做降低了FixedUpdate函數(shù)被調(diào)用的頻率以及物理引擎執(zhí)行碰撞檢測與剛體更新的頻率嘴脾。如果您使用了較低的固定增量時間男摧,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來平滑剛體組件译打。

14.減少GetComponent的調(diào)用耗拓,使用 GetComponent或內(nèi)置組件訪問器會產(chǎn)生明顯的開銷。您可以通過一次獲取組件的引用來避免開銷奏司,并將該引用分配給一個變量(有時稱為"緩存"的引用)乔询。例如,如果您使用如下的代碼:

void Update () {  
    transform.Translate(0, 1, 0);  
}
var myTransform : Transform;  
void Awake () {  
    myTransform = transform;
}  
void Update () {  
    myTransform.Translate(0, 1, 0);  
}

15.避免分配內(nèi)存韵洋,您應(yīng)該避免分配新對象竿刁,除非你真的需要,因為他們不再在使用時麻献,會增加垃圾回收系統(tǒng)的開銷们妥。您可以經(jīng)常重復(fù)使用數(shù)組和其他對象,而不是分配新的數(shù)組或?qū)ο竺阄恰_@樣做好處則是盡量減少垃圾的回收工作监婶。同時,在某些可能的情況下齿桃,您也可以使用結(jié)構(gòu)(struct)來代替類(class)惑惶。這是因為,結(jié)構(gòu)變量主要存放在棧區(qū)而非堆區(qū)短纵。因為棧的分配較快带污,并且不調(diào)用垃圾回收操作,所以當(dāng)結(jié)構(gòu)變量比較小時可以提升程序的運行性能香到。但是當(dāng)結(jié)構(gòu)體較大時鱼冀,雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會導(dǎo)致單獨的開銷悠就,實際上它可能比等效對象類的效率還要低千绪。

16.使用內(nèi)置數(shù)組,內(nèi)置數(shù)組是非常快的梗脾。ArrayList或Array類很容易使用荸型,你能輕易添加元件。但是他們有完全不同的速度炸茧。 內(nèi)置數(shù)組有固定長度瑞妇,并且大多時候你會事先知道最大長度然后填充它稿静。內(nèi)置數(shù)組最好的一點是他們直接嵌入結(jié)構(gòu)數(shù)據(jù)類型在一個緊密的緩存里,而不需要任何額外 類型信息或其他開銷辕狰。因此改备,在緩存中遍歷它是非常容易的,因為每個元素都是對齊的柳琢。

private var positions : Vector3[];
void Awake () {    
    positions = new Vector3[100];    
    for (var i=0;i<100;i++)    
        positions[i] = Vector3.zero;
}

17.使用靜態(tài)類型

使用靜態(tài)類型替代動態(tài)類型绍妨。Unity使用一種技術(shù)叫做類型推理的技術(shù)來自動轉(zhuǎn)換JavaScript為靜態(tài)類型腳本。
var foo = 5;
上面例子中的foo將自動被推斷為一個整數(shù)值柬脸。因此,Unity可能使用大量的編輯時間進(jìn)行優(yōu)化毙驯,而不使用耗時的動態(tài)名稱變量查找等倒堕。這就是為什么Unity的JavaScript執(zhí)行平均速度是其他JavaScript的20倍的原因之一。
唯一的問題是有時不是所有的東西都能做類型推斷爆价,Unity將會為這些變量重新使用動態(tài)類型垦巴。通過這樣,編寫JavaScript代碼很簡單铭段,但也會使代碼運行速度變慢骤宣。
看個例子:

void Start ()  
{  
    var foo = GetComponent(MyScript);  
    foo.DoSomething();  
}  

這里foo將是動態(tài)類型,因此呼叫函數(shù)DoSomething必須要較長的時間序愚,因為foo的類型未知憔披,它必須弄明白是否支持DoSomething函數(shù),如果支持爸吮,調(diào)用函數(shù)芬膝。

void Start ()  
{  
    MyScript foo = GetComponent(MyScript);  
    foo.DoSomething();  
} 

這里我們強制foo為指定類型,你將獲得更好的性能形娇。

18.使用#pragma strict

現(xiàn)在問題是锰霜,你通常不會意識到你在使用動態(tài)類型。#pragma strict可以解決這個問題桐早!簡單的添加#pragma strict在腳本頂部癣缅,之后Unity將禁用腳本的動態(tài)類型,強制你使用靜態(tài)類型哄酝。如果有一個類型未知友存,Unity將報告編譯錯誤。下面炫七,foo將在編 譯時報錯:

#pragma strict  
void Start ()  
{  
    var foo = GetComponent(MyScript);  
    foo.DoSomething();  
}  

19.緩存組件查找

另一個優(yōu)化是組件緩存爬立。這種優(yōu)化需要一些代碼并且不是總有必要。但是如果你的代碼真的很大万哪,并且你需要盡可能的性能提升侠驯,它會是很好的優(yōu)化抡秆。
當(dāng)你通過GetComponent獲取一個組件或一個變量時,Unity必須從游戲物體里找到正確的組件吟策。這時你便能通過一個緩存組件引用到一個私有變量儒士。
將:

void Update () {  
    transform.Translate(0, 0, 5);  
}  
  
//轉(zhuǎn)換為:  
  
private var myTransform : Transform;  
void Awake () {  
    myTransform = transform;  
}  
void Update () {  
    myTransform.Translate(0, 0, 5);  
}  

后面的代碼運行較快,因為Unity不用在每一幀尋找變換組件檩坚。同樣着撩,支持腳本組件。你可以使用GetComponent獲取組件或其他快捷屬性匾委。

20.如果沒有必要不要調(diào)用函數(shù)

最簡單拖叙,最好的優(yōu)化是執(zhí)行最少的工作。如赂乐,當(dāng)一個敵人在遠(yuǎn)處時薯鳍,讓他處于睡眠狀態(tài),大多時候是可行的挨措。直到玩家靠近挖滤,可以這樣處理:

void Update ()  
{  
// Early out if the player is too far away.    if (Vector3.Distance(transform.position, target.position) > 100)  
    return;  
    perform real work work...  
}  

這 并不是很好的方法,雖然Unity不得不在每一幀訪問update函數(shù)浅役。更好的方法是禁用這個行為直到玩家靠近斩松。有3中方法做這個:使用 OnBecameVisible和OnBecameInvisible。這些調(diào)用與渲染系統(tǒng)相聯(lián)系觉既。一旦攝像機看到物體惧盹,OnBecameVisible 將被調(diào)用,不看他時奋救,OnBecameInvisible被調(diào)用岭参。這有時很有用。但是對于AI來講通常是沒有用的尝艘,因為你背轉(zhuǎn)敵人演侯,敵人就變成不可用了。

function OnBecameVisible () {  
    enabled = true;  
}  
  
function OnBecameInvisible ()  
{  
    enabled = false;  
}  

21.使用觸發(fā)器背亥。一個簡單的球形觸發(fā)器能引發(fā)驚人效果秒际。你可以調(diào)用OnTriggerEnter/Exit,當(dāng)進(jìn)入你想要的作用范圍狡汉。

void OnTriggerEnter (Collider c)  
{  
    if (c.CompareTag("Player"))  
    enabled = true;  
}  
  
void OnTriggerExit (Collider c)  
{  
    if (c.CompareTag("Player"))  
    enabled = false;  
} 

22.使用協(xié)同程序娄徊。Update的問題是他在每幀都發(fā)生。很可能只需要5秒鐘檢查一次玩家的距離盾戴。這可以節(jié)約大量的處理周期寄锐。


原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子橄仆,更是在濱河造成了極大的恐慌剩膘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盆顾,死亡現(xiàn)場離奇詭異怠褐,居然都是意外死亡,警方通過查閱死者的電腦和手機您宪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門奈懒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宪巨,你說我怎么就攤上這事磷杏。” “怎么了捏卓?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵茴丰,是天一觀的道長。 經(jīng)常有香客問我天吓,道長,這世上最難降的妖魔是什么峦椰? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任龄寞,我火速辦了婚禮,結(jié)果婚禮上汤功,老公的妹妹穿的比我還像新娘物邑。我一直安慰自己,他們只是感情好滔金,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布色解。 她就那樣靜靜地躺著,像睡著了一般餐茵。 火紅的嫁衣襯著肌膚如雪科阎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天忿族,我揣著相機與錄音锣笨,去河邊找鬼。 笑死道批,一個胖子當(dāng)著我的面吹牛错英,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隆豹,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼椭岩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起判哥,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤献雅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姨伟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惩琉,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年夺荒,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞒渠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡技扼,死狀恐怖伍玖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剿吻,我是刑警寧澤窍箍,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站丽旅,受9級特大地震影響椰棘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榄笙,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一邪狞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茅撞,春花似錦帆卓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拄查,卻和暖如春吁津,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背靶累。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工腺毫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挣柬。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓潮酒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邪蛔。 傳聞我的和親對象是個殘疾皇子急黎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

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