程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 刨根問底U3D---從Profile中窺探Unity的內存管理,u3d---unity

刨根問底U3D---從Profile中窺探Unity的內存管理,u3d---unity

編輯:C#入門知識

刨根問底U3D---從Profile中窺探Unity的內存管理,u3d---unity


這篇文章包含哪些內容

這篇文章從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

 

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

 

3· 奇跡的事情發生了... 紋理占用的內存大小直接變為 5.8MB 

 

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果真沒有這麼傻... 以後使用NGUI制作Atlas時候也不用擔心是否需要刪除小圖這件事了.

既然知道了只有在真機設備上才可以進行測試,於是需要再將剛才做的測試重來一次了,只不過這次是在真機上面.

 

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

 

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

 

2·加入一張圖片並放置在場景上面

雖然沒有像想象中的那樣為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)時候可以釋放顯存。

 

以上是我這幾天通過實驗摸索的一些經驗,希望能對你有所幫助。如果哪裡說的不對還請大神指出

謝謝

 

Best

Eran

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved