全面理解Unity加載和內(nèi)存管理_第1頁
全面理解Unity加載和內(nèi)存管理_第2頁
全面理解Unity加載和內(nèi)存管理_第3頁
全面理解Unity加載和內(nèi)存管理_第4頁
全面理解Unity加載和內(nèi)存管理_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、全面理解 Unity 加載和內(nèi)存管理 最近一直在和這些內(nèi)容糾纏,把心得和大家共享一下 : Unity 里有兩種動態(tài)加載機制:一是 Resources.Load ,一是 通過 AssetBundle, 其實兩者本質(zhì)上我理解沒有什么區(qū)別。 Resources.Load 就是從一個缺省打進程序包里的 AssetBundle 里加載資源,而一般 AssetBundle 文件需要你 自己創(chuàng)建,運行時動態(tài)加載,可以指定路徑和來源的。 其實場景里所有靜態(tài)的對象也有這么一個加載過程,只是 Unity 后臺替你自動完成了。 詳細說一下細節(jié)概念: AssetBundle 運行時加載 : 來自文件就用 Create

2、FromFile( 注意這種方法只能用于 standalone 程序)這是最快的加載方法 也可以來自 Memory, 用 CreateFromMemory(byte), 這個 byte 可以來自文件讀取的緩沖, www 的下載或者其他可能 的方式。 其實 WWW 的 assetBundle 就是內(nèi)部數(shù)據(jù)讀取完后自動創(chuàng)建 了一個 assetBundle 而已 Create 完以后, 等于把硬盤或者網(wǎng)絡(luò)的一個文件讀到內(nèi)存一 個區(qū)域,這時候只是個 AssetBundle 內(nèi)存鏡像數(shù)據(jù)塊,還沒 有 Assets 的概念。 Assets 加載 : 用 AssetBundle.Load( 同 Resour

3、ces.Load) 這才會從 AssetBundle 的內(nèi)存鏡像里讀取并創(chuàng)建一個 Asset 對象,創(chuàng) 建 Asset 對象同時也會分配相應(yīng)內(nèi)存用于存放 ( 反序列化 ) 異步讀取用 AssetBundle.LoadAsync 也可以一次讀取多個用 AssetBundle.LoadAll AssetBundle 的釋放: AssetBundle.Unload(flase) 是釋放 AssetBundle 文件的內(nèi)存 鏡像,不包含 Load 創(chuàng)建的 Asset 內(nèi)存對象。 AssetBundle.Unload(true) 是釋放那個 AssetBundle 文件內(nèi) 存鏡像和并銷毀所有用 Load

4、 創(chuàng)建的 Asset 內(nèi)存對象。 一個 Prefab 從 assetBundle 里 Load 出來 里面可能包括: Gameobject transform mesh texture material shader script 和各種其他 Assets 。 你 Instantiate 一個 Prefab ,是一個對 Assets 進行 Clone( 復(fù) 制)+ 引用結(jié)合的過程, GameObject transform 是 Clone 是 新生成的。其他 mesh / texture / material / shader 等, 這其 中有些是純引用的關(guān)系的,包括: Texture 和 T

5、errainData , 還有引用和復(fù)制同時存在的,包括: Mesh/material/PhysicMaterial 。引用的 Asset 對象不會被復(fù) 制,只是一個簡單的指針指向已經(jīng) Load 的 Asset 對象。這 種含糊的引用加克隆的混合,大概是搞糊涂大多數(shù)人的主要 原因。 專門要提一下的是一個特殊的東西: Script Asset ,看起來很 奇怪, Unity 里每個 Script 都是一個封閉的 Class 定義而已 , 并沒有寫調(diào)用代碼,光 Class 的定義腳本是不會工作的。其 實 Unity 引擎就是那個調(diào)用代碼, Clone 一個 script asset 等 于 new

6、 一個 class 實例,實例才會完成工作。通過 AddComponent 給物體添加一個 Script Assets ,就完成了把 腳本類實例掛到 Unity 主線程的調(diào)用鏈里去的工作, Class 實例里的 OnUpdate OnStart 等才會被執(zhí)行。多個物體掛同 一個腳本,其實就是在多個物體上掛了那個腳本類的多個實 例而已,這樣就好理解了。在 new class 這個過程中,數(shù)據(jù) 區(qū)是復(fù)制的,代碼區(qū)是共享的,算是一種特殊的復(fù)制+引用 關(guān)系。 你可以再 Instantiate 一個同樣的 Prefab, 還是這套 mesh/texture/material/shader. ,這時候會有

7、新的 GameObject 等,但是不會創(chuàng)建新的引用對象比如 Texture. 所以你 Load 出來的 Assets 其實就是個數(shù)據(jù)源, 用于生成新 對象或者被引用,生成的過程可能是復(fù)制( clone) 也可能是 引用(指針) 當(dāng)你 Destroy 一個實例時,只是釋放那些 Clone 對象,并不 會釋放引用對象和 Clone 的數(shù)據(jù)源對象, Destroy 并不知道 是否還有別的 object 在引用那些對象。 等到?jīng)]有任何游戲場景物體在用這些 Assets 以后,這些 assets 就成了沒有引用的游離數(shù)據(jù)塊了,是 UnusedAssets 了,這時候就可以通過 Resources.Un

8、loadUnusedAssets 來 釋放 ,Destroy 不能完成這個任務(wù), AssetBundle.Unload(false) 也不行, AssetBundle.Unload(true) 可以但不安全, 除非你很 清楚沒有任何對象在用這些 Assets 了。 配個圖加深理解: 雖然都叫 Asset ,但復(fù)制的和引用的是不一樣的,這點被 Unity 的暗黑技術(shù)細節(jié)掩蓋了,需要自己去理解。 關(guān)于內(nèi)存管理 按照傳統(tǒng)的編程思維,最好的方法是:自己維護所有對象, 用一個 Queue 來保存所有 object, 不用時該 Destory 的,該 Unload 的自己處理。 但這樣在 C# .net

9、框架底下有點沒必要,而且很麻煩。 穩(wěn)妥起見你可以這樣管理 創(chuàng)建時: 先建立一個 AssetBundle, 無論是從 www 還是文件還是 memory 用 AssetBundle.load 加載需要的 asset 加載完后立即 AssetBundle.Unload(false), 釋放 AssetBundle 文件本身的內(nèi)存鏡像,但不銷毀加載的 Asset 對象。(這樣你不用保存 AssetBundle 的引用并且可以立即 釋放一部分內(nèi)存) 釋放時: 如果有 Instantiate 的對象,用 Destroy 進行銷毀 在合適的地方調(diào)用 Resources.UnloadUnusedAssets

10、, 釋放 已經(jīng)沒有引用的 Asset. 如果需要立即釋放內(nèi)存加上 GC.Collect() ,否則內(nèi)存未必會 立即被釋放,有時候可能導(dǎo)致內(nèi)存占用過多而引發(fā)異常。 這樣可以保證內(nèi)存始終被及時釋放,占用量最少。也不需要 對每個加載的對象進行引用。 當(dāng)然這并不是唯一的方法,只要遵循加載和釋放的原理,任 何做法都是可以的。 系統(tǒng)在加載新場景時,所有的內(nèi)存對象都會被自動銷毀,包 括你用 AssetBundle.Load 加載的對象和 Instaniate 克隆的。 但是不包括 AssetBundle 文件自身的內(nèi)存鏡像, 那個必須要 用 Unload 來釋放,用 .net 的術(shù)語,這種數(shù)據(jù)緩存是非托管

11、的。總結(jié)一下各種加載和初始化的用法 : AssetBundle.CreateFrom :創(chuàng)建一個 AssetBundle 內(nèi)存 鏡像,注意同一個 assetBundle 文件在沒有 Unload 之前不 能再次被使用 WWW.AssetBundle :同上,當(dāng)然要先 new 一個再 yield return 然后才能使用 AssetBundle.Load(name) :從 AssetBundle 讀取一個指定名 稱的 Asset 并生成 Asset 內(nèi)存對象,如果多次 Load 同名對 象,除第一次外都只會返回已經(jīng)生成的 Asset 對象,也就是 說多次 Load 一個 Asset 并不會生成

12、多個副本( singleton ) Resources.Load(path;name) :同上 ,只是從默認的位置加載。 Instantiate ( object) : Clone 一個 object 的完整結(jié)構(gòu),包括 其所有 Component 和子物體(詳見官方文檔) , 淺 Copy , 并不復(fù)制所有引用類型。有個特別用法,雖然很少這樣用, 其實可以用 Instantiate 來完整的拷貝一個引用類型的 Asset, 比如 Texture 等,要拷貝的 Texture 必須類型設(shè)置為 Read/Write able ??偨Y(jié)一下各種釋放 Destroy: 主要用于銷毀克隆對象,也可以用于場

13、景內(nèi)的靜態(tài) 物體,不會自動釋放該對象的所有引用。雖然也可以用于 Asset, 但是概念不一樣要小心,如果用于銷毀從文件加載的 Asset 對象會銷毀相應(yīng)的資源文件!但是如果銷毀的 Asset 是 Copy 的或者用腳本動態(tài)生成的,只會銷毀內(nèi)存對象。 AssetBundle.Unload(false): 釋放 AssetBundle 文件內(nèi)存鏡 像 AssetBundle.Unload(true): 釋放 AssetBundle 文件內(nèi)存鏡像 同時銷毀所有已經(jīng) Load 的 Assets 內(nèi)存對象 Reources.UnloadAsset(Object): 顯式的釋放已加載的 Asset 對象,

14、只能卸載磁盤文件加載的 Asset 對象 Resources.UnloadUnusedAssets: 用于釋放所有沒有引用的 Asset 對象 GC.Collect() 強制垃圾收集器立即釋放內(nèi)存 Unity 的 GC 功 能不算好,沒把握的時候就強制調(diào)用一下在 3.5.2 之前好像 Unity 不能顯式的釋放 Asset 舉兩個例子幫助理解 例子 1: 一個常見的錯誤:你從某個 AssetBundle 里 Load 了一個 prefab 并克隆之: obj = Instantiate(AssetBundle1.Load(MyPrefab” ); 這個 prefab 比如是個 npc 然后你不

15、需要他的時候你用了: Destroy(obj); 你以為就釋放 干凈了 其實這時候只是釋放了 Clone 對象,通過 Load 加載的所有 引用、非引用 Assets 對象全都靜靜靜的躺在內(nèi)存里。 這種情況應(yīng)該在 Destroy 以后用: AssetBundle1.Unload(true) ,徹底釋放干凈。 如果這個 AssetBundle1 是要反復(fù)讀取的 不方便 Unload , 那可以在 Destroy 以后用: Resources.UnloadUnusedAssets() 把所有和這個 npc 有關(guān) 的 Asset 都銷毀。 當(dāng)然如果這個 NPC 也是要頻繁創(chuàng)建 銷毀的 那就應(yīng)該讓那

16、些 Assets 呆在內(nèi)存里以加速游戲體驗。 由此可以解釋另一個之前有人提過的話題:為什么第一次 Instantiate 一個 Prefab 的時候都會卡一下, 因為在你第一次 Instantiate 之前,相應(yīng)的 Asset 對象還沒有被創(chuàng)建,要加載 系統(tǒng)內(nèi)置的 AssetBundle 并創(chuàng)建 Assets, 第一次以后你雖然 Destroy 了,但 Prefab 的 Assets 對象都還在內(nèi)存里,所以 就很快了。例子 2: 從磁盤讀取一個 1.unity3d 文件到內(nèi)存并建立一個 AssetBundle1 對象 AssetBundle AssetBundle1 = AssetBundle

17、.CreateFromFile(1.unity3d); 從 AssetBundle1 里讀取并創(chuàng)建一個 Texture Asset, 把 obj1 的主貼圖指向它 obj1.renderer.material.mainTexture = AssetBundle1.Load(wall) as Texture; 把 obj2 的主貼圖也指向同一個 Texture Asset obj2.renderer.material.mainTexture =obj1.renderer.material.mainTexture; Texture 是引用對象,永遠不會有自動復(fù)制的情況出現(xiàn) (除非 你真需要,用代碼

18、自己實現(xiàn) copy) ,只會是創(chuàng)建和添加引用 如果繼續(xù): AssetBundle1.Unload(true) 那 obj1 和 obj2 都變成黑的了, 因為指向的 Texture Asset 沒了 如果: AssetBundle1.Unload(false) 那 obj1 和 obj2 不變,只是 AssetBundle1 的內(nèi)存鏡像釋放了 繼續(xù): Destroy(obj1),/obj1 被釋放,但并不會釋放剛才 Load 的 Texture 如果這時候: Resources.UnloadUnusedAssets(); 不會有任何內(nèi)存釋放 因為 Texture asset 還被 obj2 用著 如果 Destroy(obj2) obj2 被釋放,但也不會釋放剛才 Load 的 Texture 繼續(xù) Resources.UnloadUnusedAssets(); 這時候剛才 load 的 Texture Asset 釋放了,因為沒有任何引 用了 最后 CG.Coll

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論