天天看點

【Unity】從Profile中窺探Unity的記憶體管理

刨根問底U3D---從Profile中窺探Unity的記憶體管理

http://www.cnblogs.com/zhepama/p/4363005.html

這篇文章包含哪些内容

這篇文章從Unity的Profile元件入手,來探讨一下Unity在開發環境和正式環境中的記憶體使用發面的一些差別,

并且給出了最好控制記憶體的方法(我想你已經知道了...Prefab ) ,以及原因。

提前需要閱讀的文章

在閱讀本文之前或之後我建議閱讀一下以下幾篇文章

雨松的

Unity3D研究院之Assetbundle的實戰 http://www.xuanyusong.com/archives/2405/

Unity3D研究院之Assetbundle的原理 http://www.xuanyusong.com/archives/2373

王巍的

Unity 3D中的記憶體管理 http://onevcat.com/2012/11/memory-in-unity3d/

星塵(不太确定是否是原作者 哈)的

Unity3D占用記憶體太大的解決方法 http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html

從NGUI的AltasPacker說起

事情的起因還是因為我在學習使用NGUI(剛接觸Unity沒幾天…), 教程看了看都沒問題 自己動手操作的時候突然發現...

NGUI 建立UI時候必須都先要建立Altas,那我建立好後的散圖是放在工程裡面呢 還是要删掉? 不删會影響性能麼? 

Google了一番并沒有發現滿意答案,是以隻好自己Profile一下,于是有了如下實驗:

1· 首先建立一個空場景,運作,打開Profile. Texture(紋理)部分使用記憶體2.5MB

【Unity】從Profile中窺探Unity的記憶體管理

2·加入一張圖檔,場景上不做任何引用。 該圖檔顯存展開後大小為1.5MB , 運作 然後再打開Profile

【Unity】從Profile中窺探Unity的記憶體管理

3· 奇迹的事情發生了... 紋理占用的記憶體大小直接變為 5.8MB 

【Unity】從Profile中窺探Unity的記憶體管理

5.8MB- 2.5MB = 3.3MB 很奇怪的一個數,于是我同時懷疑三件事

1· 即使素材完全用不到但是如果工程中有導入則最後會被打包

2· 即使素材沒有被目前場景使用,Unity仍舊會加載 ( Unity啟動時候加載所有的素材??! )

3· Unity對素材在記憶體中有留有一份引用,3.3MB ~= 1.5MBx2 ( ??! ) 

如果以上三點有一個是真的(全是錯的), 那Unity無疑直接變為廢柴.... 

于是乎就此三點我開始無限的刨根問底.... 

功夫不負有心人吧,終于在一片文章上面找到了突破口

http://answers.unity3d.com/questions/57909/find-unused-assets-in-project.html

這篇文章說的很清楚,Unity在釋出時候會自動過濾掉未引用的所有資源,并且整個被打包進來的資源可以通過Log檢視

根據文章指出的位置找到Log,并且就上面的工程進行測試,打出Android包  (Mac下Log在 /Users/eran/Library/Logs/Unity/Editor.log , eran為你的使用者名)

【Unity】從Profile中窺探Unity的記憶體管理

這樣的話 第一個心結就解開了, 看來Unity果真沒有這麼傻... 以後使用NGUI制作Atlas時候也不用擔心是否需要删除小圖這件事了.

既然知道了隻有在真機裝置上才可以進行測試,于是需要再将剛才做的測試重來一次了,隻不過這次是在真機上面.

1· 首先建立一個空場景,運作,打開Profile. Texture(紋理)部分使用記憶體153.0KB

【Unity】從Profile中窺探Unity的記憶體管理

可以注意到,在真機上面運作時候 記憶體占用明顯降低了,并且開銷的線很平,不再像編輯環境一樣會有波動.

2·加入一張圖檔并放置在場景上面

【Unity】從Profile中窺探Unity的記憶體管理

雖然沒有像想象中的那樣為1.5MB,不過 既然小于3MB,則Unity3D肯定不可能留有一份記憶體的備份(DRAM一份 VRAM一份,DRAM的用于處理LostConext時候重新上傳GPU). 

這裡面其實還有一個小插曲:

在Android上面 Rendering中顯示 使用的顯存為0,這點和我了解的3D渲染原理不符呀,一直讓我困惑了很久。後來突然想到 難道是因為手機是共享顯存的原因?

果不其然 當我把項目釋出為PC版本時候 再跑Profile,顯存占用就有數字了. 并且顯存占用數和我後面說道的動态剔除還有關系,說明顯存還發生了swap,這塊和所講的事情無關 就不細說了,如果大神對這塊很了解 希望指點我一下.

ok 繼續說上面提到一嘴的 動态剔除,這個是我無意發現的,

我把上面那張圖加了一個Animation 讓其左右移動,當我真機測試時候,當這張圖檔移出螢幕時候DrawCall會減1,也就是隻要螢幕看不到的東西Unity會自動幫你剔除,

減少DrawCall, 其實細想想, 這個是很正常的一個事情,因為Unity是一個完全的3D引擎,這也是為什麼在Unity裡面沒有像素 進來機關是Unit,沒有螢幕的寬高,隻能調整錄影機的視野. 

相比之下之前用的Starling,Cocos,雖然底層也在使用GPU進行渲染,但是他的整體引擎架構是基于2D的,是以自然無法在底層完成這種自動剔除以及顯存交換的行為.相比之下Unity要優越許多.

就此Unity的一大謎題得以解開,根據上面的實驗我得到了如下兩條結論

結論: 即使項目中有許多未使用的圖檔,隻要未放置在特殊檔案夾下(Resource,StreamingAssets)并且沒有被Prefab引用,最終導出時候不會被打包,更不會占用顯存,但是在開發階段會.

結論: Unity 會自行對移出場景的對象進行剔除進而減少DrawCall

Prefab 最好的管理記憶體(顯存)的方式

我的刨根問底行為到這裡并沒有結束, 既然知道了Unity如何加載素材,那他什麼時候解除安裝呢?

我又做了如下實驗:

建立兩個場景,SceneA,SceneB. 在SceneA中加載一張紋理,同時提供一個跳轉到SceneB的按鈕. 

點選按鈕跳轉到SceneB,SceneB是一個空場景 什麼都不放. 

預期的是當切換到SceneB時候SceneA中所占用的顯存應該會被釋放,不過結果卻又是讓人大失所望... 仍舊沒有變化

即使我在SceneB中調用GC都沒用(其實看過GC介紹的朋友也應該知道在那裡調GC本來就應該沒用)

最後又是一通Google,不過這次沒有像上次那麼走運 沒有任何的收獲,這也是我後來轉向開始研究Prefab的原因. 不過還是繼續把這裡說完. 

又是一次意外的測試,我發現當我再建立一個SceneC時候, 由SceneA->SceneB->SceneC 這個時候 SceneA中的顯存會得到釋放. 就此問題我還發了一個Question. 

有個朋友給了他項目上的證明,Unity确實如此 http://ask.unitymanual.com/question/36097

既然Unity自動管理的記憶體需要跨兩個場景才能消除,那我們有沒有辦法自己控制呢? 方法是有的 那就是使用Prefab.

如何建立及如何使用Prefab 松雨的那兩篇文章已經說的非常明白了,我就不重複造輪子了.

結論: Unity自身的顯存回收是需要經過跨兩個場景,如果使用Prefab在調用assetBundle.Unload (true)時候可以釋放顯存。

MEM