7_內(nèi)存管理
Mono
Mono框架是Unity底層的一個組成部分,與Unity配合使用
當(dāng)編寫C#代碼時,Mono充當(dāng)了C#腳本的承載環(huán)境
打包時Mono會將C#代碼編譯成中間語言
在運行時Mono會將中間語言編譯為機(jī)器碼,用來與Unity各個功能進(jìn)行交互
IL2CPP
IL2CPP是Unity后來引入的腳本運行環(huán)境,用來取代Mono
IL2CPP的主要優(yōu)勢是在性能方面
IL2CPP在打包時就會將C#代碼編譯成中間語言,之后轉(zhuǎn)化為C++代碼,并轉(zhuǎn)化為C++工程,在這個過程中IL2CPP還會對中間代碼進(jìn)行一些優(yōu)化,最后再將C++代碼編譯成原生機(jī)器碼,IL2CPP生成的機(jī)器碼是與設(shè)備平臺息息相關(guān)的
在運行時就不需要其它工序,原生機(jī)器碼會直接和Unity各個功能進(jìn)行交互,同時這樣做還可以還可以提高代碼的安全性,防止反編譯
中間語言
這是一種與具體平臺無關(guān)的中間代碼,它不依賴于具體的硬件或操作系統(tǒng)
中間語言的存在是讓Unity可以在不同平臺上運行的關(guān)鍵
托管語言
C#和C++的一個重要區(qū)別是C#是一種托管語言,而C++是一種非托管語言
托管語言意味著它的執(zhí)行是在托管環(huán)境中運行的,托管語言的特點就是可以自動進(jìn)行內(nèi)存管理,包括對象的分配和釋放,垃圾回收等,開發(fā)者不需要手動去管理內(nèi)存資源,這會犧牲一些運行速度,但也可以用更簡單的方式實現(xiàn)游戲邏輯
非托管語言需要開發(fā)者手動分配和釋放內(nèi)存,并負(fù)責(zé)管理對象的生命周期,C++可以直接訪問底層硬件和操作系統(tǒng)資源,運行速度更快,但也需要花更多的時間在資源管理上
內(nèi)存域
Unity中內(nèi)存框架可以劃分為3個不同的內(nèi)存域,每個域存儲不同的數(shù)據(jù)類型,關(guān)注不同的任務(wù)集
托管堆
這個地方是Mono平臺工作的地方
我們編寫的任何腳本都會在這里變?yōu)閷嵗瘜ο?,腳本之間的交互也是在這里進(jìn)行
被稱為托管堆是因為這里提供了自動內(nèi)存管理和垃圾回收的功能
本地域
Unity的底層代碼會在這里運行,本地域不會進(jìn)行自動的內(nèi)存管理和垃圾回收,需要開發(fā)者自行處理
外部庫
Unity的插件會在這里運行,插件的內(nèi)存管理和垃圾回收由插件的開發(fā)者負(fù)責(zé),同時插件的開發(fā)者也可能會留些接口讓其他人也可以控制插件的內(nèi)存管理
托管橋:負(fù)責(zé)連接托管代碼和底層非托管代碼的接口,當(dāng)頻繁通過托管橋調(diào)用非托管對象時會引起性能問題
運行時的內(nèi)存空間
棧
用來存儲值類型的數(shù)據(jù)和數(shù)據(jù)對象的引用
棧的內(nèi)存分配是有編譯器自動處理的,不需要開發(fā)者手動管理
堆
用來存儲引用類型的對象
當(dāng)一個對象在堆上沒有任何引用指向它時,這個對象就會成為垃圾對象,垃圾收集器會在合適的時間回收它
內(nèi)存碎片
內(nèi)存是一個連續(xù)的儲存空間,這段空間又被劃分為不同的內(nèi)存塊來儲存數(shù)據(jù)和對象
當(dāng)內(nèi)存中間被銷毀時就會產(chǎn)生兩個不連續(xù)的儲存空間,這時如果需要分配一個大塊的連續(xù)內(nèi)存,但可用的內(nèi)存又分布在不連續(xù)的區(qū)域上,就可能無法滿足連續(xù)的內(nèi)存分配需求,導(dǎo)致內(nèi)存的浪費
內(nèi)存分配流程
驗證是否有足夠的連續(xù)空間用于分配新對象
如果沒有連續(xù)空間,則檢查所有實例化的對象,并且篩選出不再被引用的對象
將不再被引用的對象進(jìn)回收
再次驗證是否有足夠大的連續(xù)的內(nèi)存空間
如果沒有,從操作系統(tǒng)請求新的內(nèi)存塊
在新分配的塊前分配新對象,并返回給調(diào)用者
內(nèi)存優(yōu)化

紋理內(nèi)存
紋理的壓縮格式
導(dǎo)入到Unity的圖片都會被轉(zhuǎn)換成可以被GPU直接讀取的圖片格式(ETC,ASTC等)
選擇合適的壓縮可是可以達(dá)到
節(jié)省內(nèi)存
減少底寬
降低加載耗時
MipMap
可以優(yōu)化遠(yuǎn)處物體的表現(xiàn)效果
減少帶寬
場景紋理需要開啟mip map,UI關(guān)閉mip map
Texture Quality(Edit - Project Settings - Quality -Texture Quality)
可以限制加載進(jìn)內(nèi)存的MipMap層數(shù),因此對開啟了MipMap的紋理生效
Full Res:加載所有層級的MipMap
Half Res:不會加載MipMap0層級,會使用MipMap1代替MipMap0
Quarter Res:不會加載MipMap0和MipMap1這兩層,當(dāng)需要使用MipMap0時會用MipMap2代替
可以在不同配置的機(jī)器上使用不同的設(shè)置,來進(jìn)行畫質(zhì)分級
Read/write
開啟了Read/write選項Unity不僅會把紋理數(shù)據(jù)上傳到GPU內(nèi)存,還會上傳到CPU內(nèi)存
沒有對紋理的讀寫需求就不要開Read/write
網(wǎng)格資源
Read/write
開啟了Read/write選項Unity不僅會把網(wǎng)格數(shù)據(jù)上傳到GPU內(nèi)存,還會上傳到CPU內(nèi)存
沒有對網(wǎng)格的讀寫需求就不要開Read/write
頂點屬性

渲染的時候Shader并不一定會用到所有種類的頂點屬性,這時多余的屬性就會造成不必要的內(nèi)存浪費

設(shè)置Normals屬性

設(shè)置Tangent屬性
骨骼

帶有動畫效果的模型必須有骨骼
靜態(tài)物體可以去掉骨骼

這樣設(shè)置就可以不導(dǎo)入模型的骨骼
可以在導(dǎo)入模型前在建模軟件中去掉骨骼
RenderTexture資源

抗鋸齒:為了減小鋸齒,抗鋸齒需要對每個像素進(jìn)行多重采樣,同時為了存儲多重采樣的結(jié)果需要建立中間緩沖區(qū),這樣就需要額外的內(nèi)存來存儲這些信息

陰影分辨率:陰影分辨率決定了陰影紋理像素的數(shù)量,而像素數(shù)量直接影響內(nèi)存占用
音頻資源

Force To Mono:開啟這個選項音頻就會變?yōu)殡p聲道,這樣內(nèi)存占用就會增加一倍
不同的音頻加載方式會影響內(nèi)存占用,可以參考之前的文章對音頻設(shè)置合適的加載方式
不同的音頻壓縮格式會影響內(nèi)存占用,可以參考之前的文章對音頻設(shè)置合適的壓縮格式
粒子系統(tǒng)
粒子內(nèi)內(nèi)存的消耗與粒子在場景中的播放數(shù)量有關(guān),低端機(jī)上可以關(guān)閉一些不必要的粒子特效以減少內(nèi)存消耗

粒子系統(tǒng)在初始化時會創(chuàng)建一個長度為最大粒子數(shù)的數(shù)組,如果實際播放的粒子數(shù)不是很多,但是最大例子數(shù)設(shè)置的很高的話,就會導(dǎo)致創(chuàng)建的數(shù)組太長,從而造成內(nèi)存的浪費