整理參考:https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games (只是看了這篇博客記錄的筆記)正則表達式
遊戲運行時使用內存來存儲數據,當這些數據再也不被使用時,存儲這些數據的內存被釋放以便於以後這些內存能夠被複用。緩存
垃圾:存儲這些無用數據的內存的術語。函數
GC(Garbage Collection):使垃圾(無用內存)能夠再次使用的過程。性能
須要優化GC的緣由:大數據
①由於GC的觸發是有條件觸發,當垃圾存儲到必定時候,會被動觸發回收這些無用內存。若是垃圾內過多,被動觸發次數過多,優化
會形成CPU負荷過多,GC是由CPU啓動的。spa
②每次觸發GC,會有明顯的卡頓,幀率下降,尤爲低端手機。若是遊戲在戰鬥時候發生卡頓,就是很很差的體驗了。操作系統
被釋放:當變量超出做用域時,該內存再也不被使用並能夠歸還給原來的內存池,當內存被歸還給原有的內存池裏。3d
被分配:變量在做用域內,分配給他的內存仍然在使用中,咱們稱這部份內存已被分配。orm
棧:Unity能夠訪問的內存池之一,用於短時間存儲小塊數據,變量超出做用域時被自動實時釋放。
堆:Unity能夠訪問的內存池之一,用於長期存儲和較大數據塊。變量超出做用域時並無被釋放,仍是繼續保持被分配狀態。
垃圾收集器(garbage collector):識別和釋放未使用的堆內存。垃圾收集器按期清理堆。
棧分配和釋放過程:棧分配和釋放簡單快速。這是由於棧只用於在短期內存儲小數據。分配和釋放老是以可預測的順序發生,
並具備可預測的大小。棧的工做方式相似於棧數據類型:這是一個簡單的元素集合,這種狀況下的內存塊,只能以嚴格的順序添加和刪除元素。這種簡單性和
嚴格性使得它變得很是快速。
堆分配過程:堆分配比棧分配複雜額多。由於堆能夠用來存儲長期和短時間數據及各類不一樣類型大小的數據。分配和釋放並不老是按可預測的順序
進行且可能須要大小差距巨大的內存塊。
當一個堆內存建立時,將執行如下步驟:
①Unity檢查堆上是否有足夠的空間內存,若是有,爲該變量被分配內存。若是沒有,Unity觸發GC試圖釋放未使用的堆內存,這個操做可能很慢。
若是GC以後堆內存足夠,則該變量被分配內存 。
②GC以後堆上仍是沒有足夠的空閒內存,Unity將向操做系統申請更多內存以擴大堆大小。這個操做可能很慢。堆分配可能會很慢,特別在必須執行GC和擴大堆大小時。
(GC是個費時的操做,堆上的對象越多,代碼中的引用數越多,GC越費時)
GC時的具體的步驟:
①垃圾收集器檢索堆上的每一個對象、
②垃圾收集器搜索全部當前對象引用以肯定堆上的對象是否仍在做用域內
③不在做用域內的對象唄標記爲刪除
④刪除被標記的對象並將內存返回給堆。
什麼時候觸發GC:
堆分配時堆上的可用內存不足時觸發GC。
GC會自動運行。(頻率因平臺而異)
手動強制調用GC。
若是堆上有不少對象和大量的對象引用要檢查,則檢查全部這些對象的過程可能很慢。這可能致使遊戲卡頓或緩慢運行。
GC在不合時宜的場合被觸發。若是CPU在咱們遊戲的性能關鍵部分已經滿負荷了,那此時即便是少許的GC額外開銷也可能致使
咱們的遊戲卡頓或運行緩慢。
堆碎片,當從堆中分配內存時,會根據必須存儲的數據大小從不一樣大小的塊中的可用空間中獲取內存。
當這些內存返回到堆時,堆可能分紅不少由分配塊分隔的小空閒塊.這意味着雖然可用內存總量很高。可是因爲
碎片化嚴重而沒法分配一塊連續的大內存。這意味着GC被觸發或不得不擴大堆大小。
(嚴重後果:①遊戲內存大小會高於實際所須要的大小 ②GC會被頻繁的觸發)
①嘗試在合適時機(loading時),手動觸發GC和擴展堆大小以便GC可控。
②緩存,將局部函數中的局部引用變量寫成公共。
③對象池。
④清理容器。
⑤字符串的建立之類。
⑥Debug.Log的引用。
⑦注意裝箱,裝箱會產生垃圾源於底層,當一個值類型變量被裝箱時,Unity在堆上建立一個臨時的System.Object
來包裝值類型變量。一個System.Object是一個引用類型的變量,因此當這個臨時對象被處理時會產生垃圾。
⑧StartCortine會產生少許垃圾。
yield return 0;//會產生垃圾,int變量0被裝箱。
=>>> yield return null
yield return new WairforSeconds(1f);//若是屢次被調用,會產生不少。
==>>>WaitForSeconds delay =new WaitForSeconds(1f);//能夠事先緩存起來
yield return delay;
⑨Linq和正則表達式在後臺有裝箱操做而產生垃圾,最好少使用。
⑩構建代碼以最小化GC的影響
代碼的構建方式可能會影響GC,即便代碼中沒有堆分配,也可能會增長GC的負擔。可能增長GC的負擔之一
是要求檢查他不應檢查的東西。Struct是值類型變量,可是若是包含一個引用類型便利的struct,那麼垃圾收集器必須檢查整個
結構體。
另一個增長GC負擔的操做是使用沒必要要的對象引用,當垃圾收集器搜索堆上對象的引用時,它必須檢查代碼中的
每一個當前對象的引用。更少的對象引用意味着更少的工做量。