C#筆記 垃圾回收garbage collection

託管堆(managed heap)
託管堆指的是c#創建引用類型變量的內存。會定時使用垃圾回收(Garbage Collect)機制來釋放不需要的內存,有必要的時候堆大小是會改變的。回收過程和改變大小會引起變量在內存裏位置的變化(所以對於引用類型c#不提供指針。同時也要注意引用類型中的值類型成員的指針使用)。
如果想要利用好這個自動管理機制,要避免對不需要的內存保留引用,否則內存不會被回收。內存也就浪費了。
對象的分配:new 和 Instantiate(Unity常用的一個操作,其實內部也是new)。
結束對象引用:設置爲null或引用其它、超出作用域、調用Destory( )。

ToString裝箱,給託管帶來的負擔
裝箱就會產生引用對象,引用對象多了就會給託管堆造成負擔,應儘量避免。
負面例子:

// 以下用法不好
public class ExampleScript : MonoBehaviour {
    void ConcatExample(int[] intArray) {
        string line = intArray[0].ToString();
        for (i = 1; i < intArray.Length; i++) {
            line += ", " + intArray[i].ToString();
        }
        return line;
    }
}

循環每次調用ToString產生堆上的引用對象然後又結束引用。類似用法應避免,使用System.Text.StringBuilder來代替。
另一個例子:

// 以下用法不好
// 每幀更新分數,也會給託管帶來負擔。
void Update() {
        string scoreText = "Score: " + score.ToString();
        scoreBoard.text = scoreText;
    }

// 應該這樣
// 加個判斷,沒必要就不更新
void Update() {
        if (score != oldScore) {
            scoreText = "Score: " + score.ToString();
            scoreBoard.text = scoreText;
            oldScore = score;
        }
    }

函數返回一個數組,給託管帶來負擔
這也是個負面的例子:

// 每次調用都會分配一塊內存
void RandomList(int numElements) {
        var result = new float[numElements];
        for (int i = 0; i < numElements; i++) {
            result[i] = Random.value;
        }
        return result;
    }

// 不會產生額外分配
void RandomList(float[] arrayToFill) {
        for (int i = 0; i < arrayToFill.Length; i++) {
            arrayToFill[i] = Random.value;
        }
    }

Garbage Collection的2個策略
這2個策略是Unity官方文檔提供的,沒必要盲目使用。
1)小heap快速且經常的GC策略。200kb的堆在iphone3G上大概需要5ms,1mb則需要7ms。

if (Time.frameCount % 30 == 0)
{
   System.GC.Collect();
}

2)大heap慢速且不經常的GC策略。避免在需要流暢運行中發生GC動作(如打鬥場景中)。以下代碼手動擴展了堆大小,從而影響系統策略。

void Start() {
        var tmp = new System.Object[1024];

        // make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks
        for (int i = 0; i < 1024; i++)
            tmp[i] = new byte[1024];

        // release reference
        tmp = null;
    }

pool
可以藉助reuseable object pools來犧牲一些內存換取效率,也就是複用gameobject(應該釋放但不釋放)。甚至有可能在需要流暢運行的場景中不調用Instantiate和Destroy來避免瞬時卡頓,可提前建立pool裏所有的gameobject。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章