Unity筆記 資源加載 內存優化

本機堆(Native Heap)
這裏是unity管理的資源內存(區別於c#的託管堆),包括裝載GameObject, Transform, Mesh, Texture, Material, Shader和Script等。這些內存是unity來管理的,看下unity默認如何組織的資源加載和卸載的。但是否和託管堆是獨立的這個有待驗證。

加載,當scene加載完成時所有用到的asset都會被加載,包括Hierarchy中的所有的gameobject涉及的資源和腳本中用到的資源。
卸載,除了DontDestroyOnLoad的資源,在切換scene時都會被卸載。注意singleton(設計模式)通常用到DontDestroyOnLoad。

對於內存優化,我們感興趣的就是看看什麼行爲可以影響unity自己的資源加載和卸載策略。
儘量減少Hierarchy中的引用,改成手動的Resource.Load()這樣加載的資源可以用Resource.UnloadAsset(obj)和Resources.UnloadUnusedAssets()來釋放。還有AssetBundle的Load和Unload方法。
這些load和unload還有Instantiate都是幹啥的,見下圖:

unity memory

看了上圖,可以理解加載的2個階段:第一階段是使用Resources.Load或者AssetBundle.Load加載各種資源,第二階段是使用GameObject.Instantiate克隆出一個新的GameObject。

3種實例化prefab的方式
1)Resouce.Load,再調用Instantiate
2)AssetBundle.Load,再調用Instantiate
3) 在Inspector裏把prefab拖到一個public變量上去,用的時候調用Instantiate

只有AssetBundle.Load的方式不會導致第一次實例化(Instantiate)的延遲。
使用Resources.Load的時候在第一次Instantiate之前,相應的Asset對象還沒有被創建,直到第一次Instantiate時纔會真正去讀取文件創建這些Assets。它的目的是實現一種OnDemand的使用方式,到該資源真正使用時纔會去創建這些資源。
而使用AssetBundle.Load方法時,會直接將資源文件讀取出來創建這些Assets,因此第一次Instantiate的代價會相對較小。

UnusedAssets
如何讓一個gameobject可以通過UnloadUnusedAssets釋放。

Object obj = Resource.Load("MyPrefab");
GameObject instance = Instantiate(obj) as GameObject;
Destroy(instance);

Resources.UnloadUnusedAssets();// 這裏不會釋放內存

obj = null;

Resources.UnloadUnusedAssets();// 這裏會釋放內存

再結合圖看一遍這些操作
Instantiate的過程是一個對Assets進行Clone(複製)和引用相結合的過程,Clone的過程需要申請內存存放自己的數據,而引用的過程只需要直接一個簡單的指針指向一個已經Load的資源即可。例如Transform是通過Clone出來的,Texture和TerrainData是通過引用複製的,而Mesh,Material,PhysicalMaterial和Script是Clone和引用同時存在的(換句話說,gameobject需要自己數據的就要clone,不需要的引用就好了)。

因此Load操作其實Load一些原型數據(Assets)出來,用於創建新對象時被Clone或者被引用。

然後是銷燬資源的過程。當Destory一個GameObject或者其他實例時,只是釋放實例中那些Clone出來的Assets,而並不會釋放那些引用的Assets,因爲Destroy不知道是否有其他人在引用這些Assets。等到場景中沒有任何物體引用到這些Assets之後,它們就會成爲UnusedAssets,此時可以通過Resources.UnloadUnusedAssets來進行釋放。AssetBundle.Unload(false)不行,因爲它只會釋放文件的內存鏡像,不會釋放資源;AssetBunde.Unload(true)也不行,因爲它是暴力的釋放,可能有其他對象在引用其中的Assets,暴力釋放可能導致程序錯誤。

另外需要注意,系統在加載新場景時,所有的內存對象都會被自動銷燬,這包括了Resources.Load加載的Assets, 靜態綁定的Assets,AssetBundle.Load加載的資源和Instantiate實例化的對象。但是AssetBundle.Load本身的文件內存鏡像(用於創建各種Asset)不會被自動銷燬,這個必須使用AssetBundle.Unload(false)來進行主動銷燬。推薦的做法是在加載完資源後立馬調用AssetBunble.Unload(false)銷燬文件內存鏡像。

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