久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Unity 3D中的內(nèi)存管理

 kiki的號 2017-06-20

本文歡迎轉(zhuǎn)載,,但煩請保留此行出處信息:http://www./2012/11/memory-in-unity3d/

Unity3D在內(nèi)存占用上一直被人詬病,特別是對于面向移動設(shè)備的游戲開發(fā),,動輒內(nèi)存占用飆上一兩百兆,,導致內(nèi)存資源耗盡,,從而被系統(tǒng)強退造成極差的體驗。類似這種情況并不少見,,但是絕大部分都是可以避免的,。雖然理論上Unity的內(nèi)存管理系統(tǒng)應當為開發(fā)者分憂解難,讓大家投身到更有意義的事情中去,,但是對于Unity對內(nèi)存的管理方式,,官方文檔中并沒有太多的說明,基本需要依靠自己摸索,。最近在接手的項目中存在嚴重的內(nèi)存問題,,在參照文檔和Unity Answer眾多猜測和證實之后,稍微總結(jié)了下Unity中的內(nèi)存的分配和管理的基本方式,,在此共享,。

雖然Unity標榜自己的內(nèi)存使用全都是“Managed Memory”,但是事實上你必須正確地使用內(nèi)存,,以保證回收機制正確運行,。如果沒有做應當做的事情,那么場景和代碼很有可能造成很多非必要內(nèi)存的占用,,這也是很多Unity開發(fā)者抱怨內(nèi)存占用太大的原因,。接下來我會介紹Unity使用內(nèi)存的種類,以及相應每個種類的優(yōu)化和使用的技巧,。遵循使用原則,,可以讓非必要資源盡快得到釋放,,從而降低內(nèi)存占用,。


Unity中的內(nèi)存種類

實際上Unity游戲使用的內(nèi)存一共有三種:程序代碼、托管堆(Managed Heap)以及本機堆(Native Heap),。

程序代碼包括了所有的Unity引擎,,使用的庫,,以及你所寫的所有的游戲代碼。在編譯后,,得到的運行文件將會被加載到設(shè)備中執(zhí)行,,并占用一定內(nèi)存。這部分內(nèi)存實際上是沒有辦法去“管理”的,,它們將在內(nèi)存中從一開始到最后一直存在,。一個空的Unity默認場景,什么代碼都不放,,在iOS設(shè)備上占用內(nèi)存應該在17MB左右,,而加上一些自己的代碼很容易就飆到20MB左右。想要減少這部分內(nèi)存的使用,,能做的就是減少使用的庫,,稍后再說,。

托管堆是被Mono使用的一部分內(nèi)存。Mono項目一個開源的.net框架的一種實現(xiàn),,對于Unity開發(fā),,其實充當了基本類庫的角色。托管堆用來存放類的實例(比如用new生成的列表,,實例中的各種聲明的變量等),。“托管”的意思是Mono“應該”自動地改變堆的大小來適應你所需要的內(nèi)存,,并且定時地使用垃圾回收(Garbage Collect)來釋放已經(jīng)不需要的內(nèi)存。關(guān)鍵在于,,有時候你會忘記清除對已經(jīng)不需要再使用的內(nèi)存的引用,,從而導致Mono認為這塊內(nèi)存一直有用,而無法回收,。

最后,,本機堆是Unity引擎進行申請和操作的地方,比如貼圖,,音效,,關(guān)卡數(shù)據(jù)等。Unity使用了自己的一套內(nèi)存管理機制來使這塊內(nèi)存具有和托管堆類似的功能,?;纠砟钍牵绻谶@個關(guān)卡里需要某個資源,,那么在需要時就加載,,之后在沒有任何引用時進行卸載。聽起來很美好也和托管堆一樣,,但是由于Unity有一套自動加載和卸載資源的機制,,讓兩者變得差別很大。自動加載資源可以為開發(fā)者省不少事兒,,但是同時也意味著開發(fā)者失去了手動管理所有加載資源的權(quán)力,,這非常容易導致大量的內(nèi)存占用(貼圖什么的你懂的),也是Unity給人留下“吃內(nèi)存”印象的罪魁禍首,。


優(yōu)化程序代碼的內(nèi)存占用

這部分的優(yōu)化相對簡單,,因為能做的事情并不多:主要就是減少打包時的引用庫,改一改build設(shè)置即可,。對于一個新項目來說不會有太大問題,,但是如果是已經(jīng)存在的項目,可能改變會導致原來所需要的庫的缺失(雖說一般來說這種可能性不大),,因此有可能無法做到最優(yōu),。

當使用Unity開發(fā)時,,默認的Mono包含庫可以說大部分用不上,在Player Setting(Edit->Project Setting->;Player或者Shift+Ctrl(Command)+B里的Player Setting按鈕)面板里,,將最下方的Optimization欄目中“Api Compatibility Level”選為.NET 2.0 Subset,,表示你只會使用到部分的.NET 2.0 Subset,不需要Unity將全部.NET的Api包含進去,。接下來的“Stripping Level”表示從build的庫中剝離的力度,,每一個剝離選項都將從打包好的庫中去掉一部分內(nèi)容。你需要保證你的代碼沒有用到這部分被剝離的功能,,選為“Use micro mscorlib”的話將使用最小的庫(一般來說也沒啥問題,,不行的話可以試試之前的兩個)。庫剝離可以極大地降低打包后的程序的尺寸以及程序代碼的內(nèi)存占用,,唯一的缺點是這個功能只支持Pro版的Unity,。

這部分優(yōu)化的力度需要根據(jù)代碼所用到的.NET的功能來進行調(diào)整,有可能不能使用Subset或者最大的剝離力度,。如果超出了限度,,很可能會在需要該功能時因為找不到相應的庫而crash掉(iOS的話很可能在Xcode編譯時就報錯了)。比較好地解決方案是仍然用最強的剝離,,并輔以較小的第三方的類庫來完成所需功能,。一個最常見問題是最大剝離時Sysytem.Xml是不被Subset和micro支持的,如果只是為了xml,,完全可以導入一個輕量級的xml庫來解決依賴(Unity官方推薦這個),。

關(guān)于每個設(shè)定對應支持的庫的詳細列表,可以在這里找到,。關(guān)于每個剝離級別到底做了什么,,Unity的文檔也有說明。實際上,,在游戲開發(fā)中絕大多數(shù)被剝離的功能使用不上的,,因此不管如何,庫剝離的優(yōu)化方法都值得一試,。


托管堆優(yōu)化

Unity有一篇不錯的關(guān)于托管堆代碼如何寫比較好的說明,,在此基礎(chǔ)上我個人有一些補充。

首先需要明確,,托管堆中存儲的是你在你的代碼中申請的內(nèi)存(不論是用js,,C#還是Boo寫的)。一般來說,,無非是new或者Instantiate兩種生成object的方法(事實上Instantiate中也是調(diào)用了new),。在接收到alloc請求后,托管堆在其上為要新生成的對象實例以及其實例變量分配內(nèi)存,如果可用空間不足,,則向系統(tǒng)申請更多空間,。

當你使用完一個實例對象之后,通常來說在腳本中就不會再有對該對象的引用了(這包括將變量設(shè)置為null或其他引用,,超出了變量的作用域,,或者對Unity對象發(fā)送Destory())。在每隔一段時間,,Mono的垃圾回收機制將檢測內(nèi)存,,將沒有再被引用的內(nèi)存釋放回收??偟膩碚f,,你要做的就是在盡可能早的時間將不需要的引用去除掉,這樣回收機制才能正確地把不需要的內(nèi)存清理出來,。但是需要注意在內(nèi)存清理時有可能造成游戲的短時間卡頓,,這將會很影響游戲體驗,因此如果有大量的內(nèi)存回收工作要進行的話,,需要盡量選擇合適的時間,。

如果在你的游戲里,,有特別多的類似實例,,并需要對它們經(jīng)常發(fā)送Destroy()的話,游戲性能上會相當難看,。比如小熊推金幣中的金幣實例,,按理說每枚金幣落下臺子后都需要對其Destory(),然后新的金幣進入臺子時又需要Instantiate,,這對性能是極大的浪費,。一種通常的做法是在不需要時,不摧毀這個GameObject,,而只是隱藏它,,并將其放入一個重用數(shù)組中。之后需要時,,再從重用數(shù)組中找到可用的實例并顯示,。這將極大地改善游戲的性能,相應的代價是消耗部分內(nèi)存,,一般來說這是可以接受的,。關(guān)于對象重用,可以參考Unity關(guān)于內(nèi)存方面的文檔中Reusable Object Pools部分,,或者Prime31有一個是用Linq來建立重用池的視頻教程(Youtube,,需要翻墻,上半部分下半部分),。

如果不是必要,,應該在游戲進行的過程中盡量減少對GameObject的Instantiate()和Destroy()調(diào)用,因為對計算資源會有很大消耗,。在便攜設(shè)備上短時間大量生成和摧毀物體的話,,很容易造成瞬時卡頓。如果內(nèi)存沒有問題的話,,盡量選擇先將他們收集起來,,然后在合適的時候(比如按暫停鍵或者是關(guān)卡切換),將它們批量地銷毀并且回收內(nèi)存,。Mono的內(nèi)存回收會在后臺自動進行,,系統(tǒng)會選擇合適的時間進行垃圾回收。在合適的時候,,也可以手動地調(diào)用System.GC.Collect()來建議系統(tǒng)進行一次垃圾回收,。要注意的是這里的調(diào)用真的僅僅只是建議,可能系統(tǒng)會在一段時間后在進行回收,,也可能完全不理會這條請求,,不過在大部分時間里,這個調(diào)用還是靠譜的,。


本機堆的優(yōu)化

當你加載完成一個Unity的scene的時候,,scene中的所有用到的asset(包括Hierarchy中所有GameObject上以及腳本中賦值了的的材質(zhì),貼圖,,動畫,,聲音等素材),都會被自動加載(這正是Unity的智能之處),。也就是說,,當關(guān)卡呈現(xiàn)在用戶面前的時候,所有Unity編輯器能認識的本關(guān)卡的資源都已經(jīng)被預先加入內(nèi)存了,,這樣在本關(guān)卡中,,用戶將有良好的體驗,不論是更換貼圖,,聲音,,還是播放動畫時,都不會有額外的加載,,這樣的代價是內(nèi)存占用將變多,。Unity最初的設(shè)計目的還是面向臺式機,幾乎無限的內(nèi)存和虛擬內(nèi)存使得這樣的占用似乎不是問題,,但是這樣的內(nèi)存策略在之后移動平臺的興起和大量移動設(shè)備游戲的制作中出現(xiàn)了弊端,,因為移動設(shè)備能使用的資源始終非常有限。因此在面向移動設(shè)備游戲的制作時,盡量減少在Hierarchy對資源的直接引用,,而是使用Resource.Load的方法,,在需要的時候從硬盤中讀取資源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()盡快將其卸載掉,??傊@里是一個處理時間和占用內(nèi)存空間的trade off,,如何達到最好的效果沒有標準答案,,需要自己權(quán)衡。

在關(guān)卡結(jié)束的時候,,這個關(guān)卡中所使用的所有資源將會被卸載掉(除非被標記了DontDestroyOnLoad)的資源,。注意不僅是DontDestroyOnLoad的資源本身,其相關(guān)的所有資源在關(guān)卡切換時都不會被卸載,。DontDestroyOnLoad一般被用來在關(guān)卡之間保存一些玩家的狀態(tài),,比如分數(shù),級別等偏向文本的信息,。如果DontDestroyOnLoad了一個包含很多資源(比如大量貼圖或者聲音等大內(nèi)存占用的東西)的話,,這部分資源在場景切換時無法卸載,將一直占用內(nèi)存,,這種情況應該盡量避免,。

另外一種需要注意的情況是腳本中對資源的引用。大部分腳本將在場景轉(zhuǎn)換時隨之失效并被回收,,但是,,在場景之間被保持的腳本不在此列(通常情況是被附著在DontDestroyOnLoad的GameObject上了),。而這些腳本很可能含有對其他物體的Component或者資源的引用,,這樣相關(guān)的資源就都得不到釋放,這絕對是不想要的情況,。另外,,static的單例(singleton)在場景切換時也不會被摧毀,同樣地,,如果這種單例含有大量的對資源的引用,,也會成為大問題。因此,,盡量減少代碼的耦合和對其他腳本的依賴是十分有必要的,。如果確實無法避免這種情況,那應當手動地對這些不再使用的引用對象調(diào)用Destroy()或者將其設(shè)置為null,。這樣在垃圾回收的時候,,這些內(nèi)存將被認為已經(jīng)無用而被回收。

需要注意的是,Unity在一個場景開始時,,根據(jù)場景構(gòu)成和引用關(guān)系所自動讀取的資源,,只有在讀取一個新的場景或者reset當前場景時,才會得到清理,。因此這部分內(nèi)存占用是不可避免的,。在小內(nèi)存環(huán)境中,這部分初始內(nèi)存的占用十分重要,,因為它決定了你的關(guān)卡是否能夠被正常加載,。因此在計算資源充足或是關(guān)卡開始之后還有機會進行加載時,盡量減少Hierarchy中的引用,,變?yōu)槭謩佑肦esource.Load,,將大大減少內(nèi)存占用。在Resource.UnloadAsset()和Resources.UnloadUnusedAssets()時,,只有那些真正沒有任何引用指向的資源會被回收,,因此請確保在資源不再使用時,將所有對該資源的引用設(shè)置為null或者Destroy,。同樣需要注意,,這兩個Unload方法僅僅對Resource.Load拿到的資源有效,而不能回收任何場景開始時自動加載的資源,。與此類似的還有AssetBundle的Load和Unload方法,,靈活使用這些手動自愿加載和卸載的方法,是優(yōu)化Unity內(nèi)存占用的不二法則~

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多