本周我們準備了兩篇 Unity/團結(jié)引擎常見問題 tips,均轉(zhuǎn)載自 Unity 社區(qū)大佬 ForgemasterGua,本篇主要解決 Addressable 的 Catalog 占用內(nèi)存過大問題。ForgemasterGua 在 Unity 中國開發(fā)者社區(qū)持續(xù)更新技術(shù)內(nèi)容中,點擊閱讀原文,前往 ForgemasterGua 的社區(qū)主頁,閱讀更多干貨文章。
初步分析問題
我們在使用 Addressable 管理 AssetBundle 打包和加載時,會用到目錄文件,也就是Catalog.json。 打開這個文件,可以看到其中包含了很多數(shù)據(jù),包括打包了哪些 Asset、生成了哪些 AssetBundle 等。 其中最大的一塊數(shù)據(jù)是一個名字叫 “m_ExtraDataString” 的字符串。 我們遇到過 Catalog.json 文件本身就有 50 多 M,其中 m_ExtraDataString 就占 30 多 M 的情況。

這個數(shù)據(jù)加載進內(nèi)存,對應(yīng)的字符串甚至可以達到 70 多 M。

我們查看 Addressable 的源碼,可以在 ContentCatalogData.cs 文件的 SetData 函數(shù)里找到 m_ExtraDataString 的賦值。

而這里寫入的數(shù)據(jù)是在 AssetBundleProviders.cs 文件中的 AssetBundleRequestOptions,其中有 hash、crc、timeout、bundle name、bundle size 等很多關(guān)于一個 AssetBundle 的信息。

分析到這里,我們就不難理解:為什么隨著項目越來越大,AssetBundle 越來越多,Catalog 文件也會隨著越來越大,占用的內(nèi)存也會越來越大了。
其實,Addressable 的官方文檔里也給出了有關(guān)的建議:

根據(jù)官方文檔的描述,AssetBundle 文件不宜過大或過小,如果每個 Asset 都單獨打包成一個 AssetBundle 文件,無疑是過小,會導(dǎo)致 Catalog 文件過大,占用內(nèi)存也會很大的問題。
深入分析問題
到這里,我們可以很自然的想到,要解決這個問題,我們應(yīng)當合理規(guī)劃 AssetBundle 的粒度,控制 AssetBundle 的大小和總數(shù)量在合理的范圍內(nèi)。
具體到 Addressable 里,可以把同一個 Group 中的 Asset 一起打包。選擇 Bundle Mode 為 Pack Together 。

同時,Internal Asset Naming Mode 選擇 GUID 而不是文件的路徑,Bundle Naming Mode 選 Use Hash of Filename 也會減少 catalog.json 文件的大小。
Compress Catalog 選項,可以把 Catalog 從 Json 格式轉(zhuǎn)換成二進制格式,也能在一定程度上減小文件大小。

這些方法都能在一定程度上緩解這個問題。
但對于某些項目,要做到合理規(guī)劃 AssetBundle 的粒度是很難的,尤其是已經(jīng)開發(fā)了很久很難重構(gòu)的項目,我們就需要尋求其他解決方案。
開頭時我們提到過,m_ExtraDataString 數(shù)據(jù)占用的內(nèi)存可以達到 70 多 M。它對應(yīng)的數(shù)據(jù)結(jié)構(gòu)是 AssetBundleRequestOptions。在 MemoryProfiler 中,我們除了能找到這塊 m_ExtraDataString 數(shù)據(jù),也能找到根據(jù)這個字符串序列化好的數(shù)據(jù)結(jié)構(gòu)。

我們可以看到這里有 33680 個 AssetBundle 文件,他們的 AssetBundleRequestOptions 一共占了 2.3 MB,不算大。
解決問題
我們最終需要的是這個只占 2.3M 的數(shù)據(jù)結(jié)構(gòu),至于占了 72MB 的字符串,只是序列化之前的數(shù)據(jù)。那么,我們能不能在得到了 AssetBundleRequestOptions 數(shù)據(jù)后,就釋放掉這塊 m_ExtraDataString 字符串的內(nèi)存呢?
基于以上想法,我做了如下的嘗試:

通過Addressables.LoadContentCatalogAsync這個 API 加載 Catalog 文件后,就立刻釋放掉這個 OperationHandle。 這么做之后,我們重新抓取內(nèi)存快照,我們依然能找到 AssetBundleRequestOptions 這塊內(nèi)存,但 m_ExtraDataString 這塊內(nèi)存已經(jīng)沒有了。

通過這種方式,我們?nèi)サ袅?catalog 以字符串形式存在的內(nèi)存占用,保留了最終序列化好的數(shù)據(jù)部分。用 Addressable 官方例子 https://github.com/Unity-Technologies/Addressables-Sample 中的 Addressable Variants 工程 TextureScalerScene 驗證,這種方式?jīng)]有導(dǎo)致功能失常,內(nèi)存占用也確實減少了。

Unity 官方微信
第一時間了解Unity引擎動向,學(xué)習(xí)進階開發(fā)技能
每一個“點贊”、“在看”,都是我們前進的動力
熱門跟貼