1. 程式人生 > 實用技巧 >CocosCreator客戶端優化系列(三):記憶體優化

CocosCreator客戶端優化系列(三):記憶體優化

轉載請保留原文連結:https://blog.csdn.net/zzx023/article/details/88822815

靜態資源的記憶體管理

可以參考官方文件:管理場景

靜態資源指的是場景中直接或間接引用到的所有資源(指令碼動態載入的資源不算在內)

在場景資源的屬性編輯器中可以勾選“自動釋放資源”選項,從而在切換場景時,會自動將舊場景使用的靜態資源釋放掉,從而節省記憶體的佔用

動態資源的記憶體管理

  1. 動態資源的載入

動態資源統一使用cc.loader進行資源的載入以及管理。
參考:動態載入
可以使用的api:

cc.loader.load
cc.loader.loadRes
cc.loader.loadResArray
cc.loader.loadResDir
  • 1
  • 2
  • 3
  • 4

詳細的api使用參考[官方文件],這裡就不細說了(https://docs.cocos.com/creator/api/zh/classes/loader.html#load)

要注意的一點是,CocosCreator中通過cc.loader去載入資源的所有方法,都是非同步的
所以需要在回撥中,確認載入完成後才能使用資源。

也可以通過

cc.loader.getRes
  • 1

這個api去同步的獲取資源,但需要對get到的資源進行檢查,如果沒有載入或者沒有載入完成,則需要等待或者通過cc.loader進行載入。
這樣的話整個程式碼會清晰一些,避免掉入js的回撥地獄中
例如:

通過簡單的封裝兩個方法,在使用時可以保持程式碼的整潔易讀

另外一點需要注意的是,當批量進行載入時,cc.loader也提供了onProgress回撥,這個回撥中的三個引數中

totalCount並不是指的是載入的資源總個數,而是載入這個資源所需要載入的依賴項個數。
比如載入一個SpriteFrame,它的totalCount就是3,這3個item分別為:json,texture2D和SpriteFrame
所以當totalCount為0時,並不是代表載入資源總個數為0,而是意味著這些資源已經載入過在記憶體中了,可以直接使用。

  1. 動態資源的釋放
    動態資源的釋放同樣時通過cc.loader去管理的。
    有兩種釋放資源的管理方式:
    (1)自動釋放資源
    我們可以通過

    cc.loader.setAutoRelease
    cc.loader.setAutoReleaseRecursively
    
    • 1
    • 2

    來管理資源的自動釋放設定

    當場景切換時,由於資源已經釋放,指令碼中如果保留了引用,此時該引用將會變為非法引用。可以使用setAutoRelease和setAutoReleaseRecursively來保留這些資源。

    setAutoReleaseRecursively指定資源及資源遞迴引用到的所有資源

    預設情況下,當載入新場景時,舊場景的資源根據舊場景是否勾選“Auto Release Assets”,將會被釋放或者保留。 而使用 cc.loader.loadRes 或 cc.loader.loadResDir 動態載入的資源,則不受場景設定的影響,預設不自動釋放。
    使用這個 API 可以在單個資源上改變這個預設行為,強制在切換場景時保留或者釋放指定資源。

    (2)手動釋放資源
    cc.loader提供了以下的api,都可以去釋放通過cc.loader載入進來的資源記憶體。

    cc.loader.release
    cc.loader.releaseRes
    cc.loader.releaseAsset
    cc.loader.releaseAll
    
    • 1
    • 2
    • 3
    • 4

    這裡常用的時cc.loader.release

    不推薦使用cc.loader.releaseAll
    因為cc.loader.releaseAll會將所有通過cc.loader載入進來的動態資源全部釋放掉,而在我們正常的專案開發過程中,基本上很少有場景沒有使用到動態資源的情況,所以這種釋放通常會帶來程式的崩潰,因此使用上不推薦,最好不要使用。

    cc.loader.release在釋放資源時注意配合

    cc.loader.getDependsRecursively
    
    • 1

    使用。

    例如在release一個prefab資源時,如果只是通過

    cc.loader.release(this.prefab);
    
    • 1

    的話,這樣只會release掉這個prefab所使用的json檔案,而prefab所引用的spriteFrame以及其他的一些資源並不會釋放掉,這樣就有可能會造成記憶體的垃圾,長時間佔用記憶體

    配合getDependsRecursively就可以正確的銷燬掉prefab

    在銷燬prefab時也可以配合自動釋放資源功能,將一些特殊資源保留在記憶體中,避免重複載入

如何找到“垃圾”

隨著遊戲的執行,總會有一些記憶體是長時間不使用,同時又因為我們疏忽從而造成記憶體上的佔用以及浪費。
通過google提供的snapshot以及Allocation Profile工具可以幫助我們找到這些垃圾

查詢記憶體“垃圾”過程中需要注意的是:

  1. 注意簡單的物件建立,例如陣列,{}
  2. 注意不要忽略匿名函式
  3. 注意匿名函式使用的外部變數將被匿名函式持有
  4. 利用Timeline 觀察 GC 呼叫頻率
  5. 尋找長期不釋放的藍色記憶體

複用一切可複用的物件

最後是記憶體使用的一個理念:複用一切可複用的物件

複用,並不僅僅是為了節省物件在alloc造成的開銷,更重要的是避免GC時帶來的額外開銷。

像一些戰鬥中的掉血數字,敵人的血槽,怪物,子彈,英雄頭像等等,都是我們常常回去做複用的地方。

但還有一些地方我們不能忽略,比如:
基礎物件:cc.v2,cc.color 等
陣列和物件 {}

對於常見一些複雜物件,我們可以使用物件池NodePool進行復用
對於基礎的物件我們可以直接進行賦值從而達到複用的目的
參考引擎原始碼:

只有在不穿入out的時候new新的物件出來,正常情況下的使用均是在原有的out上進行復用

var v1;
v.add(cc.v2(5, 5), v1);
//to do....
v.add(cc.v2(10, 10), v1);
//to do....
  • 1
  • 2
  • 3
  • 4
  • 5

需要注意的是:注意閉包,注意複用時避免洩漏為全域性變數