1. 程式人生 > >我所理解的Cocos2d-x Cocos2d-x 記憶體管理機制

我所理解的Cocos2d-x Cocos2d-x 記憶體管理機制

C++記憶體管理

C++顯式堆記憶體管理

效能上有一定優勢,但有如下缺點:

  • 野指標:指標指向的內容已經被釋放,但是其他指標還可能指向它。
  • 重複釋放:重複釋放一個已經釋放的記憶體單元,或者釋放一個野指標,都會導致C++執行時錯誤。
  • 記憶體洩露:不再被使用的記憶體單元如果不被釋放,就會一直佔用記憶體單元。

C++11智慧指標

主要有如下三種:

  • unique_ptr:不能與其他智慧指標共享所指物件的記憶體。一旦轉移成功,就不能使用原來的指標,否則會導致執行時錯誤。
  • shared_ptr:共享同一堆分配物件的記憶體,它在實現上採用引用指標。只有引用計數為零時,才會真正釋放佔有的堆記憶體。
  • weak_ptr:指向shared_ptr指標分配的物件記憶體,但不擁有該記憶體。我們可以使用其lock成員來訪問其指向記憶體的一個shared_ptr物件,當其所指向的記憶體無效時,返回空值(nullptr)。weak_ptr指標通常可以用來驗證shared_ptr指標的有效性。

為什麼Cocos2d-x不使用智慧指標

有如下理由:

  1. 智慧指標有較大的效能損失。
  2. 它需要程式設計師顯示地宣告智慧指標。
  3. 在需要引用的地方,一般應該使用weak_ptr指標,否則在Node被移除的時候還要手動減持shared_ptr指標的引用計數。

程式設計師每天都面對程式碼,他們需要更自然的記憶體管理方式,就像語言自身的特性一樣,甚至幾乎察覺不到背後的機制。

C++垃圾回收機制

  • 基於引用計數:當物件被引用的次數變為零時,該物件即被視作垃圾而被回收。
  • 基於跟蹤處理:先產生跟蹤物件的關係圖,再進行垃圾回收。

不管採用哪種方式,垃圾回收機制都可以使記憶體管理變得更自然。

Cocos2d-x記憶體管理

引用計數

Cocos2d-x中的所有物件幾乎都繼承自Ref基類,Ref基類的主要職責就是對物件進行引用計數管理。

  • 當一個物件使用new運算子分配記憶體時,引用計數為1。
  • 呼叫retain()方法會增加其引用計數。
  • 呼叫release()方法會減少其引用計數。並且在其引用計數為0時自動呼叫delete運算子刪除物件並釋放記憶體。

Ref的引用計數並不是執行緒安全的。在多執行緒中,我們需要通過處理互斥鎖來保證執行緒的安全。

自動回收池(AutoreleasePool)

Cocos2d-x在每一幀結束的時候清理當前AutoreleasePool中的物件,因此可以使用autorelease()方法來宣告一個物件指標加入AutoreleasePool。

為了化簡這種宣告,Cocos2d-x使用靜態的create()方法來呼叫加入AutoreleasePool。同時,自定義的UI元素也應該遵循這樣的風格。

AutoreleasePool佇列

對於一些遊戲物件而言,一幀的生命週期有些長。我們需要能夠自定義AutoreleasePool的生命周長。AutoreleasePool在建構函式中將自身指標新增到PoolManager的AutoreleasePool佇列中,並在解構函式中從佇列中移除自己。通過在函式開頭定義AutoreleasePool物件,就能控制Cocos2d-x中的autorelease物件的宣告週期了。

不要動態分配AutoreleasePool物件,而始終使用自動變數。

Cocos2d-x中的智慧指標

Cocos2d-x 3.1 引入了智慧指標RefPtr<T>,是基於RAII實現的。

  1. 建構函式
    RefPtr需要依賴Ref的引用計數來管理記憶體,因此所有型別T必須是Ref型別。

    • 對於使用左值作為建構函式的引數,會使得引用計數加1。
    • 而使用右值作為建構函式的引數,不會使得引用計數加1。而是使用了移動複製建構函式,將其對應的記憶體佔用轉移過來。
  2. 賦值操作符

    • 對於使用左值來賦值,會使得引用計數加1。
    • 而使用右值來賦值,不會使得引用計數加1。而是使用了移動複製建構函式,將其對應的記憶體佔用轉移過來。不過,有一點不同的是會釋放之前舊的資源的引用計數。
  3. 弱引用賦值

    RefPtr通過提供一個weakAssign()方法來實現弱引用。
    但是在析構的時候,依然會release()一次。
    有什麼用呢?示例如下:

    void a()  
    {  
        RefPtr<Texture2D> l;  
        l.weakAssign(new Texture2D);  
        // -- doSomething  
    
        return;  
    }

    在函式中並沒有delete,但是依舊不會造成記憶體洩露。

  4. 解構函式:對_ptr進行safe delete。

  5. 過載了“*”和“->”操作符,使得其能夠直接訪問資源的地址。另外也可以通過get()方法來訪問資源的地址。

  6. RefPtr也過載了bool()操作符,使我們可以直接判斷其有效性。

  1. Cocos2d-x自動回收池和智慧指標該用誰?
    作者的建議是,所有的UI元素都需要使用autorelease來管理,而遊戲中的資料則使用智慧智慧RefPtr來管理。
  2. RefPtr的缺陷
    1. 可以從外部修改引用計數。
    2. 雖然RefPtr提供了一種弱引用,不對其進行引用計數增加,但仍然表現為一個強型別智慧指標的行為,它仍然可以對其資源進行修改。