1. 程式人生 > >Unity的記憶體優化

Unity的記憶體優化

今天我也偷個懶,來翻譯一篇不錯的博文,不過本人英語水平有限,翻譯不當之處萬望指教,在下當即改正,原文地址《Reducing Memory Usage in Unity, C# and .NET/Mono》(需要翻牆)

Unity的iOS版使用的是早期版本的Mono的堆管理器,而這個管理器並不會進行碎片管理,即使當你的堆充滿碎片,也只是重新new一個給你。我印象裡Unity的開發人員在嘗試設計一套新的堆管理器來解決這個問題。但是目前為止即使你的遊戲沒有記憶體洩漏,也會因為不斷增長的記憶體消耗而有當機的隱患。

C#語言一大好處便是可以在快速的編寫功能程式碼同時還能擁有很好的可讀性,但是另一個不可避免的問題則是C#語言的機制會讓你在不知不覺中編寫產生大量需要垃圾回收的程式碼,而解決這一問題的唯一辦法就是減少你對堆的使用,這裡我列出了一些可以規避這一現象的注意事項。

最終的效果會讓你的C#程式碼看上去更像C++程式碼,可能會有一些C#語言的便利你將無法享受到,但是得到的卻是效能上提升,可以讓你的程式跑的更快~

要做到的這一點,Unity profiler是個很好的工具來幫你發現問題。開啟profiler,運行遊戲,選擇CPU profiler,點選GC Alloc一列,來尋找問題最嚴重的函式,我們可以從這些肇事者們來入手尋找提升效能的空間。

  • 避免使用foreach() : 因為它會呼叫GetEnumerator(),從而在迴圈的過程中在堆中產生enumerator物件,而這些物件並無他用,所以我們應當使用傳統的for函式來完成工作以避免額外的記憶體負擔。
  • 避免使用strings : 在.NET中strings是不可變長並且是在堆中申請的。而且你不能像在C語言中的方式一樣去修改它。對於UI,使用StringBuilder來建立strings是一種比較有效率的做法,而且應當在儘量晚的時候才進行轉換。這並不影響你使用它們作為關鍵字索引來使用,因為遊標是會找到記憶體中的例項所在,但是請避免過多的修改它們。
  • 使用structs : 在mono中structs是在棧中申請的。如果你有一個工具類同時僅僅是在區域性範圍使用的,那麼你可以考慮把它變成struct。要記住structs是傳值的,所以需要通過引用來避免額外的拷貝花銷。
  • 使用structs來代替執行範圍內的固定陣列
     : 如果你的類的方法中有一些固定大小的陣列,那麼不妨使用structs或者成員陣列來代替它,這樣不必每次執行函式都申請變數,尤其是如果這些方法需要被呼叫成百上千遍。
  • 把List作為引用引數傳入函式而不是新建立一個再返回 : 聽上去彷彿沒有節約什麼,因為傳入的List同樣需要申請記憶體對嗎?但我們這麼做的原因是為了下一個看上去並不漂亮的優化。
  • 使用成員變數來代替高頻率出現的區域性變數 : 如果你的函式每次執行時都需要一個很大的List,那麼不妨將這個List設為成員變數,這樣List可以獨立於函式執行,在需要時你可以通過Clear()函式來清空List,而在C#中Clear()並不會真正的將你的記憶體空間刪除,雖然會有礙於程式碼的美觀,但是可以極大減輕效能上的負擔。
  • 避免IEnumerable擴充套件函式 : 無可厚非IEnumerable擴充套件函式雖然使用方便,但是卻會帶來更多的記憶體負擔。所以和foreach()同樣道理,我們應當盡力避免在程式碼中使用IEnumerable<>介面,你可以用IList<>來代替。
  • 減少使用函式指標 : 使用委託或者Func<>會為你的程式帶來新的記憶體分配,可是實際上卻也找不到更好的辦法來實現同樣的功能,尤其是它會為你程式在解耦帶來巨大的好處,但是總而言之要儘量精簡。

  • 要注意cloned材質 : 如果你想獲取renderer的material屬性,那麼你需要注意即使你不想修改任何東西,系統也會複製一份material給你,而且這個material不是自動回收的,只有在你切換場景或者手動呼叫Resources.UnloadUnusedAssets()才會被釋放。如果你不需要修改材質,請通過myRenderer.sharedMaterial來訪問材質。


轉載自http://www.ownself.org/blog/2013/unity-de-nei-cun-you-hua.html