夢織網(wǎng)站短視頻seo推廣
內(nèi)存(Memory)
unity 內(nèi)存部分也是優(yōu)化過程中非常重要的一個(gè)環(huán)節(jié),也會(huì)影像渲染過程中的同步等待與帶寬問題。因此內(nèi)存的優(yōu)化也可能會(huì)給我們渲染開銷帶來精簡,今天我們先來了解unity中的內(nèi)存與使用到的內(nèi)存工具。
Unity中的內(nèi)存
- 托管內(nèi)存:主要是指使用托管堆或者垃圾收集器自動(dòng)分配和管理的內(nèi)存也包括腳本堆棧與虛擬機(jī)內(nèi)存。
- C#非托管內(nèi)存:可以在C#與Unity Collection名字空間和包結(jié)合使用,不使用垃圾收集器管理的內(nèi)存部分。如果使用數(shù)據(jù)結(jié)構(gòu),不建議使用system下的collection的數(shù)據(jù)結(jié)構(gòu),而是要使用unity collection下的數(shù)據(jù)結(jié)構(gòu)進(jìn)行開發(fā)。
- Native 內(nèi)存:Unity 用于運(yùn)行引擎的C++內(nèi)存。
性能分析工具
下面我們聊一聊unity引擎提供了哪兒些內(nèi)存方面的工具
- Unity Profiler下的Memory標(biāo)簽:這里顯示了unity當(dāng)下內(nèi)存使用的追蹤狀態(tài),包括各類內(nèi)存的分配和使用情況,以及當(dāng)前unity下分配的對(duì)象與資源占用的內(nèi)存情況。2021以后版本,profiler不再提供對(duì)象抓取快照功能了。而是使用memory profiler 直接抓取內(nèi)存快照了。
- Memory Profiler:我們可以通過它來抓取內(nèi)存快照,也可以對(duì)比兩個(gè)內(nèi)存快照下unity對(duì)象與資源的差異。通過Tree Map來查看內(nèi)存分配的視圖。通過Object and Allocation標(biāo)簽查看具體對(duì)象內(nèi)存快照,并可以通過鏈接直接找到原工程中對(duì)應(yīng)的資源和對(duì)象,通過fragmentation標(biāo)簽可以查看內(nèi)存片段,unity2022以后 memory profiler變得更加簡潔了,針對(duì)native內(nèi)存甚至可以查看到具體是分配到哪兒個(gè)allocators中的。
- Memory Settings中的設(shè)置:大家現(xiàn)在可以在權(quán)衡時(shí)間和空間維度上的性能指標(biāo)做更精準(zhǔn)的設(shè)置了。
- UPR中的內(nèi)存快照功能:主要是針對(duì)移動(dòng)設(shè)備,當(dāng)你使用UPR做性能調(diào)試時(shí)會(huì)經(jīng)常用到,它可以脫離unity編輯器,在運(yùn)行時(shí)抓取內(nèi)存信息與對(duì)象分配信息,并可以做到多幀對(duì)比。具體操作請(qǐng)參閱UPR手冊(cè),后期我們可能會(huì)單獨(dú)做寫一個(gè)帖子,純翻譯供大家閱讀。
- mac或者ios上我們可以選擇xcode提供的instrument下的allocation工具。
- android上可以使用android相關(guān)的系統(tǒng)命令或者android studio profiler工具。
Profiler-Memory
Total Memory Breakdown(總內(nèi)存分解)
- ManagedHeap:托管堆,重點(diǎn)監(jiān)控對(duì)象,不要讓它超過20MB,否則可能會(huì)有性能問題!
- Graphics & Graphics Driver:驅(qū)動(dòng)程序在紋理、渲染目標(biāo)、著色器和網(wǎng)格數(shù)據(jù)上使用的估計(jì)內(nèi)存量。
- Audio:音效及聲音文件,重點(diǎn)優(yōu)化對(duì)象,播放時(shí)長較長的音樂文件需要進(jìn)行壓縮成.mp3或.ogg格式,時(shí)長較短的音效文件可以使用.wav 或.aiff格式。
- Video:視頻系統(tǒng)的估計(jì)內(nèi)存使用量。
- Other:顯示Unity跟蹤的本機(jī)內(nèi)存,但不在特定計(jì)數(shù)器下報(bào)告。
- Profiler:探查器功能從系統(tǒng)中使用和保留的內(nèi)存。
Objects Status(對(duì)象狀態(tài),顯示通常占用大量內(nèi)存的資源類型(紋理,網(wǎng)格,材質(zhì),動(dòng)畫剪輯)的對(duì)象實(shí)例數(shù)量,以及它們?cè)趦?nèi)存中的累積大小(資源,游戲?qū)ο?#xff0c;場景對(duì)象))
-
Texture2D: 2D貼圖及紋理。重點(diǎn)優(yōu)化對(duì)象,有以下幾點(diǎn)可以優(yōu)化:
1.許多貼圖采用的Format格式是ARGB 32 bit所以保真度很高但占用的內(nèi)存也很大。在不失真的前提下,適當(dāng)壓縮貼圖,使用ARGB 16 bit就會(huì)減少一倍,如果繼續(xù)Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed PVRTC 4 bits),又可以再減少一倍。把不需要透貼但有alpha通道的貼圖,全都轉(zhuǎn)換格式Android:RGB Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。
2.當(dāng)加載一個(gè)新的Prefab或貼圖,不及時(shí)回收,它就會(huì)永駐在內(nèi)存中,就算切換場景也不會(huì)銷毀。應(yīng)該確定物體不再使用或長時(shí)間不使用就先把物體制空(null),然后調(diào)用Resources.UnloadUnusedAssets(),才能真正釋放內(nèi)存。
3.有大量空白的圖集貼圖,可以用TexturePacker等工具進(jìn)行優(yōu)化或考慮合并到其他圖集中。
4.存在空白的或者純色的貼圖,可以用顏色節(jié)點(diǎn)代替
- Mesh:場景中使用的網(wǎng)格模型,注意網(wǎng)格模型的點(diǎn)面數(shù),能合并的mesh盡量合并。
- Materials:加載的材質(zhì)和它們使用的內(nèi)存的總數(shù)。
- AnimationClip: 加載的動(dòng)畫和它們使用的內(nèi)存的總數(shù)。
Assets(已加載資產(chǎn)的總數(shù))
- Game Objects:游戲?qū)ο罂倲?shù)
- Scene Objects:這個(gè)數(shù)字包括游戲?qū)ο蟮臄?shù)量,加上組件的總數(shù),以及場景中所有不屬于資產(chǎn)的東西。
- GC allocated in frame:顯示選定幀中托管分配的數(shù)量及其總大小(以字節(jié)為單位)。
Memory Profiler
主要用來查看托管內(nèi)存和本機(jī)內(nèi)存的詳細(xì)分配情況。它通過捕獲、檢查、比對(duì)內(nèi)存快照的方式來檢測內(nèi)存泄漏和內(nèi)存碎片。本篇文章中使用的版本是0.7.1版本。
安裝
add PackageManager- >Add By Name- >輸入com.unity.memoryprofiler
?
查看
Windows - > Analysis - > Memory Profiler
Memory Profiler界面,可以鏈接真機(jī)檢測,也可以在Editor檢測。
點(diǎn)擊Capture? New Snapshot截取保存當(dāng)下幀的內(nèi)容
點(diǎn)擊上圖中3號(hào)的位置Snap來查看詳細(xì)的內(nèi)容
通過觀察,我們能看到上圖的數(shù)據(jù)與Profiler中的Memory的數(shù)據(jù)是一致的
單幀檢測
一般去看工程內(nèi)的資源, 去檢查占用內(nèi)存特別大的游戲?qū)ο蟆?/p>
Memory Breakdowns界面可以查看unity內(nèi)的具體游戲?qū)ο?#xff0c;也同樣可以進(jìn)行篩選
TreeMap界面進(jìn)行檢查, 這里已經(jīng)分好類, 同時(shí)可以根據(jù)Size的大小進(jìn)行排序,查看內(nèi)存占用較大的游戲?qū)ο筮M(jìn)行優(yōu)化處理。
Fragmentation 頁簽進(jìn)行查看, 點(diǎn)擊對(duì)應(yīng)的地址塊,下方可顯示詳細(xì)信息。
1.該視圖會(huì)將內(nèi)存數(shù)據(jù)可視化成虛擬內(nèi)存布局。如下圖所示:
?2.每一行都會(huì)顯示一個(gè)內(nèi)存塊和起始地址標(biāo)簽。當(dāng)起始地址標(biāo)簽上面有黑色背景時(shí),就表明該起始地址就是內(nèi)存塊的開始部分,并且與之前的內(nèi)存塊之間存在不連續(xù)性;否則,就表明該起始地址就是內(nèi)存塊的一部分。如下圖所示:
3.既可以通過單擊起始地址標(biāo)簽來選擇關(guān)聯(lián)的內(nèi)存塊;也可以通過單擊鼠標(biāo)拖動(dòng)的方式來選擇感興趣的內(nèi)存塊;甚至可以通過單擊內(nèi)存塊中的虛擬內(nèi)存來選擇該內(nèi)存塊。
4.當(dāng)選擇內(nèi)存塊時(shí),就會(huì)在Filters面板中將相關(guān)的虛擬內(nèi)存按照指定的列表類型(區(qū)域列表-Regions list、分配列表-Allocations list、對(duì)象列表-Objects list)進(jìn)行展示詳細(xì)信息。
區(qū)域列表類型展示信息如下圖所示:
分配列表類型展示信息如下圖所示:
還有對(duì)象列表類型展示信息,操作同上。
Objects and Allocations頁面可查看詳細(xì)的對(duì)比內(nèi)容,可以進(jìn)行篩選
篩選方式:Select Table View
篩選之后就可以進(jìn)行詳細(xì)分析了,可以通過Type,Size,?Referenced By等標(biāo)簽查看對(duì)應(yīng)的游戲?qū)ο蟆?/p>
也可以鼠標(biāo)右鍵點(diǎn)擊下圖1或者2來對(duì)類型和名字進(jìn)行具體篩選。
兩幀對(duì)比檢測
一般使用兩幀率對(duì)比用于檢測內(nèi)存泄漏。
在要對(duì)比的節(jié)點(diǎn)分別進(jìn)行Capture? New Snapshot截取, 點(diǎn)擊Compare Snapshots進(jìn)行對(duì)比,在分別點(diǎn)擊兩個(gè)Snap,進(jìn)行對(duì)比。
Summary頁簽可看匯總的對(duì)比內(nèi)容:
Objects and Allocations頁面可查看詳細(xì)的對(duì)比內(nèi)容,可以進(jìn)行篩選
篩選方式:Select Table View來查看以下幾種類型數(shù)據(jù):
- [Diff] Raw Data:從原始數(shù)據(jù)列表中選擇一項(xiàng)原始數(shù)據(jù)(Root Reference、Native Allocation、Native Object等)進(jìn)行查看。
- [Diff] All Managed Objects:查看所有的托管對(duì)象(IL2CPP、Mono)。
- [Diff] All Native Objects:查看所有繼承自Unity.Object類型的本機(jī)對(duì)象。
- [Diff] All Objects:查看所有本機(jī)對(duì)象和托管對(duì)象。
- [Diff] Alloc:從分配列表中選擇一項(xiàng)分配數(shù)據(jù)(ByNativeObject、ByRoot、ByMemRegion)進(jìn)行查看。
篩選之后就可以進(jìn)行詳細(xì)分析了,可以通過Type,Size,?Referenced By等標(biāo)簽查看對(duì)應(yīng)的游戲?qū)ο蟆?/p>
也可以鼠標(biāo)右鍵來對(duì)類型和名字進(jìn)行具體篩選。
總結(jié)
MemoryProfiler 是一個(gè)非常好用的檢查內(nèi)存問題的工具,以下問題都可以通過該工具進(jìn)行排查
- 查找有問題的游戲資源,例如:Mesh和貼圖非常大的美術(shù)資源
- 內(nèi)存泄漏問題
檢測內(nèi)存占用:可以使用Unity Memory Profiler來檢測托管內(nèi)存和本機(jī)內(nèi)存的占用情況。檢測流程如下所示:
- 首先打開Unity Memory Profiler窗口;然后打開想要檢查的內(nèi)存快照;最后在主視圖區(qū)域以樹形視圖的方式來顯示內(nèi)存快照中深度內(nèi)存數(shù)據(jù)。
- 查看樹形視圖中不同的對(duì)象類別。
- 單擊樹形視圖中某一個(gè)對(duì)象類別,此時(shí)會(huì)展開該對(duì)象類別中所有的對(duì)象以及在主視圖區(qū)域下方以對(duì)象表格的方式來顯示該對(duì)象類別中所有的對(duì)象。
- 單擊對(duì)象類別中某一個(gè)對(duì)象或者單擊對(duì)象表格中某一個(gè)對(duì)象,進(jìn)而可以在對(duì)象表格中查看該對(duì)象的具體信息。
- 首先將對(duì)象表格中所有的對(duì)象按照從高到低的順序進(jìn)行排序;然后優(yōu)先從紋理、著色器變體、預(yù)分配緩沖區(qū)這三種對(duì)象來制定好減少內(nèi)存的目標(biāo)。
檢測內(nèi)存泄漏:可以使用Unity Memory Profiler來檢測托管內(nèi)存和本機(jī)內(nèi)存的泄漏情況。如下所示:
1.出現(xiàn)內(nèi)存泄漏的危害如下所示:
- 應(yīng)用程序可能因?yàn)镚C遍歷對(duì)象時(shí)間變長的原因而出現(xiàn)卡頓現(xiàn)象。
- 應(yīng)用程序可能因?yàn)榭捎脙?nèi)存空間不足的原因而出現(xiàn)閃退現(xiàn)象。
2.出現(xiàn)內(nèi)存泄漏的原因如下所示:
- 對(duì)于自動(dòng)垃圾回收而言,對(duì)象的引用計(jì)數(shù)不為0。
- 對(duì)于被動(dòng)垃圾回收而言,對(duì)象沒有被代碼手動(dòng)釋放。
3.查找并修復(fù)場景卸載后發(fā)生的內(nèi)存泄漏:流程如下所示:
- 使用Unity Memory Profiler來設(shè)置捕獲目標(biāo)。
- 首先在捕獲目標(biāo)上加載一個(gè)空?qǐng)鼍?#xff1b;然后在該場景上拍攝一張內(nèi)存快照。
- 首先在捕獲目標(biāo)上加載一個(gè)要檢測內(nèi)存泄漏的場景;然后在該場景上執(zhí)行業(yè)務(wù)模塊;最后將該場景卸載(調(diào)用Resources.UnloadUnusedAssets函數(shù))掉或者切換到一個(gè)空?qǐng)鼍啊?/li>
- 在捕獲目標(biāo)上再拍攝一張內(nèi)存快照。
- 為了避免處理內(nèi)存快照文件和捕獲目標(biāo)之間競爭系統(tǒng)資源,建議此時(shí)關(guān)閉掉捕獲目標(biāo)。
- 首先在工作臺(tái)區(qū)域打開第一張和第二張內(nèi)存快照文件;然后單擊Diff按鈕來對(duì)兩個(gè)打開的內(nèi)存快照進(jìn)行差異比對(duì);最后將差異比對(duì)生成的數(shù)據(jù)顯示在主視圖區(qū)域中。
- 首先在主視圖區(qū)域中選擇Diff表格屬性;然后選擇Group排序規(guī)則來將相同值(Deleted、New、Same)的對(duì)象合并在一個(gè)組內(nèi);最后查看數(shù)值為New的分組,如果存在對(duì)象是在第二張內(nèi)存快照中的話,就表明該對(duì)象的內(nèi)存泄漏了。
4.查找并修復(fù)小的連續(xù)分配可能造成的內(nèi)存泄漏:流程如下所示:
- 使用Unity Memory Profiler來設(shè)置捕獲目標(biāo)。
- 首先在捕獲目標(biāo)上加載一個(gè)要檢測內(nèi)存泄漏的場景;然后在該場景上拍攝第一張內(nèi)存快照。
- 首先播放要檢測內(nèi)存泄漏的場景;接著在該場景上拍攝第二張內(nèi)存快照;然后繼續(xù)播放該場景;最后在該場景上拍攝第三張內(nèi)存快照。
- 為了避免處理內(nèi)存快照文件和捕獲目標(biāo)之間競爭系統(tǒng)資源,建議此時(shí)關(guān)閉掉捕獲目標(biāo)。
- 首先在工作臺(tái)區(qū)域打開拍攝的第二張和第三張內(nèi)存快照文件;然后單擊Diff按鈕來對(duì)兩個(gè)打開的內(nèi)存快照進(jìn)行差異比對(duì);最后將差異比對(duì)生成的數(shù)據(jù)顯示在主視圖區(qū)域中。
- 首先在主視圖區(qū)域中選擇Diff表格屬性;然后選擇Group排序規(guī)則來將相同值(Deleted、New、Same)的對(duì)象合并在一個(gè)組內(nèi)。
- 首先在主視圖中選擇Owned Size表格屬性;然后選擇Group和Sort Descending排序規(guī)則來將相同值的對(duì)象合并在一個(gè)組內(nèi),并按照從大到小的順序來排列組。
- 查看較大內(nèi)存分配組中的對(duì)象是否同時(shí)存在于Same組和New組中,記錄好滿足條件的對(duì)象。
- 首先在工作臺(tái)區(qū)域打開拍攝的第一張和第二張內(nèi)存快照文件;接著單擊Diff按鈕來對(duì)兩個(gè)打開的內(nèi)存快照進(jìn)行差異比對(duì);然后將差異比對(duì)生成的數(shù)據(jù)顯示在主視圖區(qū)域中;最后執(zhí)行4.6 ~ 4.8步驟,進(jìn)而了解系統(tǒng)內(nèi)潛在的內(nèi)存泄漏。
元數(shù)據(jù):如下所示:
1.元數(shù)據(jù)類型為MetaData,包含的字段如下所示:
- content:包含項(xiàng)目名稱和捕獲目標(biāo)為Unity Editor時(shí)的腳本版本。
- platform:應(yīng)用程序?qū)?yīng)的目標(biāo)平臺(tái)。
- screenshot:針對(duì)捕獲目標(biāo)截取的屏幕截圖(像素大小小于480x240)。
2.首先在捕獲目標(biāo)上拍攝內(nèi)存快照時(shí)就會(huì)生成元數(shù)據(jù);然后該元數(shù)據(jù)會(huì)自動(dòng)添加到內(nèi)存快照中;最后開發(fā)人員可以通過元數(shù)據(jù)來更好地了解內(nèi)存快照的內(nèi)容。
3.拍攝內(nèi)存快照的方式如下所示:
- 當(dāng)項(xiàng)目中有安裝Unity Memory Profiler時(shí),此時(shí)就可以在工具欄區(qū)域中點(diǎn)擊Capture控件來針對(duì)捕獲目標(biāo)來拍攝一張內(nèi)存快照。
- 在代碼中通過MemoryProfiler.TakeSnapshot/TakeTempSnapshot函數(shù)來針對(duì)捕獲目標(biāo)拍攝一張內(nèi)存快照。在調(diào)用該函數(shù)時(shí),可以設(shè)置包含內(nèi)存快照文件路徑字符串和是否拍攝成功布爾值兩個(gè)參數(shù)的結(jié)束回調(diào)函數(shù)。
4.生成元數(shù)據(jù)的方式如下所示:
- 當(dāng)項(xiàng)目中沒有安裝Unity Memory Profiler時(shí),此時(shí)可以首先給MemoryProfiler.createMetaData委托注冊(cè)一個(gè)監(jiān)聽函數(shù);然后在該監(jiān)聽函數(shù)中設(shè)置元數(shù)據(jù)。
- 當(dāng)項(xiàng)目中有安裝Unity Memory Profiler時(shí),此時(shí)就會(huì)生成默認(rèn)的元數(shù)據(jù)。
- 當(dāng)項(xiàng)目中有安裝Unity Memory Profiler時(shí),此時(shí)就可以首先創(chuàng)建一個(gè)繼承自MetadataCollect類型的元數(shù)據(jù)收集類型;然后在該類型里面重寫CollectMetadata函數(shù);最后在該函數(shù)中設(shè)置元數(shù)據(jù)。
項(xiàng)目中可能遇到的問題
首先要明確一點(diǎn),在Editor中運(yùn)行時(shí),“Unity”大是正常的,因?yàn)樵贓ditor中運(yùn)行項(xiàng)目時(shí),引擎包含了所有的資源占用的內(nèi)存(除了部分紋理和Mesh是在GFX中),同時(shí)自身會(huì)進(jìn)行很多的輔助操作來記錄各種游戲運(yùn)行信息。一般來說,在查看游戲運(yùn)行時(shí)的真實(shí)消耗內(nèi)存,我們均是推薦直接在發(fā)布游戲上通過Profiler進(jìn)行查看,在Editor中運(yùn)行游戲所看到的內(nèi)存是要大很多的。
1.Device.Present:
- GPU的presentdevice確實(shí)非常耗時(shí),一般出現(xiàn)在使用了非常復(fù)雜的shader.
- GPU運(yùn)行的非???#xff0c;而由于Vsync的原因,使得它需要等待較長的時(shí)間.
- 同樣是Vsync的原因,但其他線程非常耗時(shí),所以導(dǎo)致該等待時(shí)間很長,比如:過量AssetBundle加載時(shí)容易出現(xiàn)該問題.
- Shader.CreateGPUProgram:Shader在runtime階段(非預(yù)加載)會(huì)出現(xiàn)卡頓(華為K3V2芯片).
- StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或類似API造成,游戲發(fā)布后需將Debug API進(jìn)行屏蔽。
2.Overhead:
- 一般情況為Vsync所致.
- 通常出現(xiàn)在Android設(shè)備上.
3.GC.Collect:
原因:
- 代碼分配內(nèi)存過量(惡性的)
- 一定時(shí)間間隔由系統(tǒng)調(diào)用(良性的).
占用時(shí)間:
- 與現(xiàn)有Garbage size相關(guān)
- 與剩余內(nèi)存使用顆粒相關(guān)(比如場景物件過多,利用率低的情況下,GC釋放后需要做內(nèi)存重排)
4.GarbageCollectAssetsProfile:
- 引擎在執(zhí)行UnloadUnusedAssets操作(該操作是比較耗時(shí)的,建議在切場景的時(shí)候進(jìn)行)。
- 盡可能地避免使用Unity內(nèi)建GUI,避免GUI.Repaint過渡GCAllow.
- if(other.tag == a.tag)改為other.CompareTag(a.tag).因?yàn)閛ther.tag為產(chǎn)生180B的GC Allow.
- 少用foreach,因?yàn)槊看蝔oreach為產(chǎn)生一個(gè)enumerator(約16B的內(nèi)存分配),盡量改為for.
- Lambda表達(dá)式,使用不當(dāng)會(huì)產(chǎn)生內(nèi)存泄漏.
5.盡量少用LINQ:
- 部分功能無法在某些平臺(tái)使用.
- 會(huì)分配大量GC Allow.
6.控制StartCoroutine的次數(shù):
- 開啟一個(gè)Coroutine(協(xié)程),至少分配37B的內(nèi)存.
- Coroutine類的實(shí)例 -> 21B.
- Enumerator -> 16B.
7.使用StringBuilder替代字符串直接連接.
8.緩存組件:
- 每次GetComponent均會(huì)分配一定的GC Allow.
- 每次Object.name都會(huì)分配39B的堆內(nèi)存.
9.ManagedHeap.UsedSize是項(xiàng)目邏輯代碼在運(yùn)行時(shí)申請(qǐng)的堆內(nèi)存,該選項(xiàng)只能通過優(yōu)化代碼來進(jìn)行降低。 優(yōu)化方法一般如下:
- 盡可能地復(fù)用變量,減少new的次數(shù);
- 使用StringBuilder代替String連接,使用for代替foreach;
- 對(duì)于局部變量或非常駐變量,盡可能使用Struct來代替Class。
ManagedHeap.UsedSize過大,一方面可能會(huì)影響一次GC的耗時(shí);另一方面也可能反映出腳本中不合理的GC Alloc。
10.有些小伙伴會(huì)發(fā)現(xiàn)System.ExecutableAndDlls占內(nèi)存巨大,且一直在增長,是怎么回事?
System.ExecutableAndDlls該項(xiàng)顯示的是執(zhí)行文件和所調(diào)用的庫(物理、渲染、IO等系統(tǒng)庫)的總和。開發(fā)團(tuán)隊(duì)不用太擔(dān)心該選項(xiàng)的數(shù)值,因?yàn)楹芏鄳?yīng)用均在共用這些庫,并且它對(duì)于真實(shí)項(xiàng)目的內(nèi)存壓力非常小,幾乎沒有影響,而且OS也不會(huì)因?yàn)樵搩?nèi)存而殺掉游戲或應(yīng)用。
11.凡是在Unity Profiler中能看到的資源就會(huì)保留在內(nèi)存中。對(duì)于這種資源,在切換場景時(shí)調(diào)一下UnloadUnusedAssets API就可以釋放。
12.Profiler.BeginSample統(tǒng)計(jì)到的數(shù)據(jù)與直接看Memory下的不一樣,前者比后者的數(shù)據(jù)更大,這怎么理解?
這種情況確實(shí)也是經(jīng)常會(huì)遇到的。一幀中分配如此高的內(nèi)存是會(huì)觸發(fā)GC.Collect的,而Mono中顯示的數(shù)值則是GC之后的Mono內(nèi)存數(shù)值。
13.正常情況下游戲如果一直玩下去,Mono是不是會(huì)一直增加? 比如頻繁打開一個(gè)界面,界面里有腳本會(huì)不斷創(chuàng)建一些東西 ,那么Mono是否會(huì)不斷增加?對(duì)性能上會(huì)不會(huì)造成影響呢?
在除開啟IL2CPP功能的應(yīng)用中,Mono 確實(shí)是不會(huì)下降,但并不應(yīng)該一直上升。
創(chuàng)建出來的東西,如果被引用在一個(gè)容器里,或者被某些腳本的變量引用,那么這部分堆內(nèi)存就釋放不掉;但如果沒有被任何容器或者變量引用(比如,臨時(shí)拼一個(gè) String),那么這部分堆內(nèi)存會(huì)在 GC 的時(shí)候釋放(釋放是指變?yōu)榭臻e的堆內(nèi)存,堆內(nèi)存的總量是不會(huì)下降的)。
對(duì)于后者,頻繁地 new 對(duì)象雖然不會(huì)一直增加堆內(nèi)存,但是會(huì)加速 GC 調(diào)用的頻率,所以同樣是需要盡量避免的。
14:我想請(qǐng)教一下,下圖這個(gè)函數(shù)中,每次我都申請(qǐng)了一個(gè)List temp = list();在這里存放6KB的數(shù)據(jù),但是如果不做GC處理,這6KB是否就一直累加,直到做GC處理了才會(huì)釋放掉,是這樣么?如果調(diào)用次數(shù)很多,每次都調(diào)用一點(diǎn)點(diǎn),也會(huì)推高內(nèi)存占用嗎?
是的,這個(gè)6KB堆內(nèi)存會(huì)隨著Update的執(zhí)行一直分配內(nèi)存,所累積的堆內(nèi)存會(huì)在GC觸發(fā)時(shí)進(jìn)行銷毀。一般來說,研發(fā)團(tuán)隊(duì)需要盡可能避免在高頻次調(diào)用函數(shù)中進(jìn)行堆內(nèi)存的分配。
15:在進(jìn)行內(nèi)存優(yōu)化時(shí),Unity Profiler給出的數(shù)據(jù)和Android系統(tǒng)(adb dumpsys meminfo,已經(jīng)考慮memtrack的影響 )的數(shù)據(jù)差距較大(已經(jīng)分析了Profiler自身的內(nèi)存占用),如何分析這部分差異,比如包括對(duì)顯存消耗進(jìn)行準(zhǔn)確統(tǒng)計(jì),OS消耗的統(tǒng)計(jì)等等?
內(nèi)存差異較大是正常的,一般來說,Profiler統(tǒng)計(jì)的內(nèi)存較為一致,而Android系統(tǒng)通過ADB反饋的PSS、Private Dirty等值則是差別很大。這主要是因?yàn)樾酒蚈S的不同而導(dǎo)致。具體的Android內(nèi)存,建議直接查看Google Android OS的相關(guān)文檔。
Unity Profiler反饋的則是引擎的真實(shí)物理使用內(nèi)存,一般我們都建議通過Profiler來查看內(nèi)存是否存在冗余、泄露等問題。
16:已經(jīng)預(yù)加載怪物,然后顯示怪物 PSS上升,并且在隱藏怪物后并沒有下降,這是什么原因?qū)е?#xff1f;顯存上去了嗎?
僅僅隱藏怪物的話,內(nèi)存是不會(huì)下降的。因?yàn)殡[藏只是改變了GameObject的狀態(tài),并沒有對(duì)內(nèi)存中的Object和資源進(jìn)行移除。同時(shí),即使是提前加載了怪物,也依然可能存在以上問題,因?yàn)槟承┵Y源是在顯示的時(shí)候,才會(huì)傳輸一份到GPU的,比如Mesh。一般情況下,顯存都不會(huì)即刻降低,這個(gè)是由Graphics Driver來管理的。建議可以看Profiler是否增長,如果Profiler沒有問題而PSS持續(xù)增長,就有可能發(fā)生了內(nèi)存泄露。
對(duì)于這個(gè)問題,建議查看《性能優(yōu)化,進(jìn)無止境---內(nèi)存篇(下)》加深理解。
17:對(duì)于Handheld.PlayFullScreenMovie 這個(gè)Unity播放開場動(dòng)畫的API,會(huì)有內(nèi)存問題嗎?比如我的mp4動(dòng)畫有20MB,那么這個(gè)動(dòng)畫會(huì)撐高mono堆內(nèi)存嗎?
Android上PlayFullScreenMovie 的實(shí)現(xiàn)實(shí)際上是通過Android原生的接口直接播放的,播放過程中Unity也是停止更新的,因此這部分的內(nèi)存理論上并不會(huì)記錄在 Unity 中,同樣也不影響Mono。
18:Texture占用內(nèi)存總是雙倍,這個(gè)是我們自己的問題,還是Unity引擎的機(jī)制?
出現(xiàn)這種情況的原因有兩種:一種是你在真機(jī)運(yùn)行時(shí)開啟了Read&Write。另一種可能是Unity的Bug,目前的Unity 5.2.3 release note如下 :
(735644) -?OpenGL: Fixed texture memory usage reporting in profiler, was twice the actual size for most textures.
開發(fā)者需要關(guān)注下自己的開發(fā)版本,5.2.3以前類似情況的項(xiàng)目可以參考一下。
19:如果腳本引用了GameObject,那轉(zhuǎn)換場景的時(shí)候腳本和GameObject都沒了,還會(huì)產(chǎn)生堆內(nèi)存的嗎?
如果腳本是MonoBehaviour,而且在切換場景后所掛的Game Object被釋放了,那么這個(gè)腳本對(duì)象所引用的堆內(nèi)存就會(huì)在GC的時(shí)候被釋放。 但有一種例外,如果是通過Static變量引用的堆內(nèi)存,那么依然是釋放不掉的,除非手動(dòng)解開引用,比如變量置Null,數(shù)組Clear等等。
移動(dòng)平臺(tái)內(nèi)存經(jīng)驗(yàn)數(shù)據(jù)參考
Textures:80M-160M
Mesh:50M-70M
Render Textures:50M-80M
AnimationClips:30M-60M
Audio:10M-20M
Cubemap:0-50M
Font:5M-15M
Shader:20M-40M
System.xxx總和:15M-30M
AssetBundle:0-10M
其他各類對(duì)象單項(xiàng):0-10M ,數(shù)量小于10000
ReservedMono:<100M
ReservedGFX:<300M
ReservedTotal:<650M
這些指標(biāo)的上下限分別代表了在移動(dòng)設(shè)備上的高低配數(shù)據(jù)的差異,其中Render Texture會(huì)根據(jù)目標(biāo)設(shè)備的分辨率的不同會(huì)有差異變化。這里給出的是1080P分辨率下的經(jīng)驗(yàn)數(shù)據(jù)指標(biāo)。一些下限為零的指標(biāo)為不使用此功能,可能沒有這方面的開銷數(shù)據(jù),如果各個(gè)指標(biāo)都在上述范圍內(nèi),不優(yōu)化也沒有問題。
移動(dòng)平臺(tái)其他經(jīng)驗(yàn)數(shù)據(jù)參考
DrawCall:300-600
SetPassCall:80-120
Triangles Count:60W-100W
Material Count:200-400
建議你的游戲相關(guān)指標(biāo)也控制在此范圍內(nèi),當(dāng)然數(shù)據(jù)僅供參考。
在我的文章里你可能會(huì)看到重復(fù)的內(nèi)容,原因是我的文章很多都是各路大神的心得,會(huì)有重復(fù)的,我沒有刪除,我覺得重復(fù)的多代表重要。
今天是2024年12月16日
重復(fù)一段毒雞湯來勉勵(lì)我和你
你的對(duì)手在看書
你的仇人在磨刀
你的閨蜜在減肥
隔壁的老王在練腰
而你在干嘛?