做Unity小游戲的開發者,大概都遇到過這樣的困境:明明在編輯器里跑得好好的,打包成WebGL上線后,卻出現掉幀、發熱、啟動慢、甚至閃退的問題。用戶一卡就流失,好不容易拉來的流量也留不住。這篇文章,就從小游戲的底層原理講起,幫你一步步定位性能瓶頸,把這些“隱形殺手”逐個擊破。
![]()
01
前言
本章從“什么是小游戲”出發,簡要介紹小游戲的概念、適用范圍與開發原理,并梳理Unity小游戲性能優化的必要性及問題定位的基本思路,為后續各章節的深入討論提供背景框架。
1.1 什么是小游戲
“小游戲”是一個較為寬泛的概念。廣義上,它通常指輕量、上手快、適合碎片化時間游玩的游戲;而對開發者而言,它更多是指依托于特定平臺運行的游戲應用。例如微信在2017年推出的爆款小游戲“跳一跳”,無需額外安裝,直接在微信內即可游玩。如今,微信、抖音、快手、QQ、支付寶等多個平臺都提供了小游戲入口,品類也日益豐富。
這類基于常用平臺的小游戲往往無需下載、安裝或卸載,即點即玩,體驗更輕便。除即時體驗外,小游戲還能借助平臺的賬號體系、好友關系等能力,具備較強的社交屬性,例如排行榜與邀請等功能。相較于APP手游,小游戲在類型上更偏輕松休閑,但也不乏玩法更復雜的重度產品。近年來,不少APP游戲也移植了小游戲版本,借助平臺流量實現快速獲量與更高的用戶留存。
1.2 適用范圍
本文主要針對Unity WebGL轉微信小游戲(下文簡稱微小)的性能優化場景,文中的經驗數據、工具鏈說明和優化建議大多基于此方案。部分結論(如性能差異倍數、內存閾值、WASM編譯內存估算等)不一定適用于Cocos/Laya/Godot等其他引擎,也不一定適用于抖音、快手等非微信平臺。讀者在參考時請結合自身項目的引擎、宿主平臺和運行環境具體分析。
1.3 小游戲的開發原理
各類小游戲平臺通常面向多樣化的開發需求,支持開發者使用不同的游戲引擎或技術方案進行開發,常見包括Unity、Cocos、Godot、Laya等。小游戲平臺并不直接綁定具體的游戲引擎,而是以引擎構建的產物作為接入與運行的基礎。各引擎均有對小游戲的適配與支持,能輸出合適的產物在小游戲環境中運行,具體適配原理可參考對應引擎的官方文檔,此處不再展開。當前簡譜主要針對使用Unity引擎開發并轉換的小游戲項目。
以上常見的游戲引擎可分為兩類:基于JavaScript/TypeScript的HTML5游戲引擎,以及原生目標的游戲引擎,Unity屬于后者。兩種類型的適配方式有所不同。對于Unity項目而言,適配方式基于WebAssembly技術,將游戲代碼導出為WASM代碼,通過膠水層代碼運行在瀏覽器環境中。這種方式的優勢在于保持原有引擎工具鏈和技術棧不變,開發者無需重寫游戲核心邏輯,通過轉換工具即可完成小游戲適配。
1.4 為什么要關注Unity小游戲的性能
玩家遇到掉幀、卡頓、發熱、閃退等性能問題時,無論游戲美術是否精良、玩法是否有趣,體驗都會直接變差,黏性隨之下降。小游戲易上手、節奏快,遭遇性能問題后玩家流失會更快。
此外,相比于APP,小游戲即開即用的特點使玩家對啟動耗時更加敏感 —— 從點開到進入正式場景的時間過長,用戶流失的可能性就會越大。
從技術角度來看,Unity小游戲以WebAssembly(WASM)+ WebGL為核心技術方案,運行性能會極大影響可承載的游戲內容玩法。開發者需要關注不同技術棧帶來的性能差異,以及不同系統平臺(Android、iOS、Windows PC)之間的性能差異。
1.4.1 Unity WebGL與APP的運行性能差異
CPU性能差異:
Unity WebGL以WASM虛擬機的形式運行在類瀏覽器環境中,CPU算力受限于虛擬機的執行效率。
在多數小游戲運行環境和Unity WebGL轉小游戲方案中,常規C# 多線程/Job多線程能力受限,不能按原生APP的線程模型使用,導致AI、動畫、渲染等模塊無法獲得多線程加速。新版Unity Web平臺已提供WebAssembly threads支持入口,但標準C# 多線程仍有限制,且依賴瀏覽器/宿主對SharedArrayBuffer等能力的支持。
這是導致Unity WebGL與APP存在性能差距的最主要因素。作為常見經驗值/保守預算,Unity WebGL通常約為APP手游性能的1/3,開發者應特別關注CPU側的性能瓶頸。但需明確,不同機型、Unity版本、是否開啟iOS高性能模式、業務瓶頸在CPU還是GPU,都會顯著影響這一比例。
GPU性能差異:
Unity以WebGL API進行渲染,其中WebGL 1.0相當于OpenGL ES 2.0,WebGL 2.0相當于OpenGL ES 3.0。
WebGL在原生渲染API之上封裝存在少量開銷,但基本渲染能力與原生APP接近。
GPU Instancing、SRP Batcher等渲染特性需要WebGL 2.0。當APP手游使用了這些特性而小游戲未開啟WebGL 2.0時,性能差距會進一步拉大。
1.4.2 WASM與JS的運行差異
WASM是靜態類型的二進制指令格式(虛擬機字節碼),其類型系統使JIT優化能更準確地預判運行期類型,因此能更快達到JIT指令優化后的峰值。在計算密集型、類型穩定的場景中,WASM通常更容易獲得穩定性能,但具體差距依賴引擎、宿主、代碼形態和平臺,不能簡單以固定倍數對比。
Unity引擎目前未針對瀏覽器環境做充分優化(如WASM與宿主接口互調頻次偏高、未做充分的代碼路徑裁剪),整體較為臃腫,部分應用場景反而遜于JS的輕量實現。
因此,兩者在實際使用中不能簡單以語言算力對比,需以實測游戲為準。
1.4.3 系統平臺之間的性能差異
Android與Windows PC:Android平臺(以微信小游戲為例,通常基于XWeb/Chromium體系,WASM虛擬機內核大體可理解為V8)與Windows PC均使用V8作為WASM虛擬機內核,均支持JIT,在相同算力條件下兩者性能接近。但需注意移動平臺散熱更差,對性能要求更苛刻。不同平臺和宿主版本的實際內核可能存在差異,不宜一概而論。
iOS:iOS默認為普通模式,不支持JIT,可用于超休閑游戲;中重度游戲建議開啟iOS高性能模式以支持JIT,但該模式需要更多精力進行調優,尤其在啟動發燙與內存方面。
iOS高性能模式:
https://developers.weixin.qq.com/minigame/dev/guide/game-engine/unity-webgl-transform/Design/iOSOptimization.html
1.5 如何確定問題
定位思路
發現性能問題后的第一步就是確定問題。如果閱讀過UWA提供的Unity移動端性能優化簡譜,讀者可能已對Unity常見性能問題有一定認知。在小游戲側,移動端的知識體系大部分仍然適用,但也有不少小游戲獨有的問題需要特別留意。總之,確定問題本身也是一項挑戰。
可用工具
工欲善其事必先利其器,直接精確的性能數據能更直觀地反映性能瓶頸,事半功倍。從小游戲角度,可用工具主要分為三個層面:
平臺層:各小游戲官方平臺基本都提供自帶的性能調試與監控工具(如微信/抖音開發者工具)。
引擎層:Unity Profiler、Memory Profiler等,用于定位引擎內部的CPU與內存瓶頸。
系統層:Android Studio Profiler、Xcode Instruments等,用于從進程視角分析整體資源消耗。
此外,UWA已對微信、抖音等小游戲平臺提供支持。UWA GOT Online工具可通過SDK快速集成到測試項目中,真機測試完成后在極短時間內完成數據上傳與解析,自動生成一系列可視化圖表。同時,基于UWA豐富的優化經驗和數據庫進行評分,針對各性能模塊提供分析建議和參數變化趨勢。
通用原則——PC開發者工具 vs 真機測試:小游戲開發中,PC端開發者工具的便捷性很容易讓人依賴其數據進行性能判定,但工具環境與真機環境在多方面存在顯著差異,貫穿CPU、內存、GPU、啟動耗時等所有性能維度。例如:紋理壓縮格式(ASTC等)在PC上會回退為RGBA32導致內存虛高、WASM編譯與JIT行為在iOS/Android真機上與PC模擬差異明顯、PC的高性能CPU會掩蓋真機上更易暴露的瓶頸。因此,任何時候都應優先以真機測試數據為最終依據,PC開發者工具僅用于初步調試和功能驗證。后續各章節中也會反復強調這一原則。
問題定位檢查清單
確認問題現象:掉幀/卡頓/發熱/閃退/啟動慢,明確優化目標
使用平臺調試工具(微信/抖音開發者工具)獲取初步性能數據
使用Unity Profiler/Memory Profiler定位引擎層瓶頸
真機測試(Android + iOS),勿僅依賴PC開發者工具數據
借助UWA GOT Online等第三方工具獲取深度分析報告與優化建議
對比APP版本與原生性能基線,區分通用問題與小游戲特有問題
02
啟動耗時
啟動耗時是玩家對小游戲最直接的性能第一印象。本章圍繞啟動流程中的三個核心環節 —— 首包資源下載、WASM代碼下載與編譯、引擎初始化及首幀邏輯 —— 逐一分析各階段的優化方法,并匯總可落地的檢查清單。
2.1 啟動流程概覽
當玩家上手一款小游戲時,最先影響到體驗的性能指標就是啟動耗時。在小游戲快節奏的環境下,啟動過長可能導致玩家尚未體驗游戲內容就已流失。根據微信小游戲官方數據,普通小游戲啟動時間為7~10s,不經優化的Unity WebGL游戲啟動可達該時間的2~3倍以上。優化目標是將首屏啟動耗時控制在5~10s甚至更短。
以微信小游戲(以下簡稱微小)為例,Unity WebGL轉換的微信小游戲主要依靠Unity Loader進行初始化,其工作流程如下:
![]()
不同平臺小游戲的啟動細節可能略有差異,但總體可歸納為三個核心環節:首包資源下載、WASM代碼下載和編譯、引擎初始化與開發者首幀邏輯。
此外,UWA GOT Online的小游戲報告中也提供了啟動耗時的相關指標、推薦值和優化建議,可幫助快速定位耗時較高的階段:
![]()
![]()
2.2 首包資源下載
首包是什么:一般名稱為xxx.webgl.data.unityweb.bin.txt,存放在CDN服務器上。首次進入游戲時下載、解壓后保持在內存,二次啟動可直接走緩存。因此對啟動耗時而言,主要關注首次下載或首包更新時的下載耗時。
首包內容構成:首包文件大小直接影響下載耗時,可使用AssetStudio查看其中包含的資源,通常包括:
Unity Default Resources — 引擎默認資源(Arial字體、默認Mesh、紋理等)
IL2CPP Meta Data — C# 代碼經IL2CPP生成的類、方法等元信息
unity_builtin_extra — Always Included的Shader
BuildSettings中所有Active的場景
Resources文件夾中的資源及其引用的其他資源
全局設置及引用到的資源(如Splash圖片等)
優化方法:
1. 排查與精簡:結合AssetStudio查看首包內容,排查其中內存占用較高或不符合預期出現的資源,盡量只保留首場景中的必要資源。下圖為AssetStudio中看到的部分首包內容。
![]()
舉例來說,加載過程的提示文字可能會使用自定義字體,如果用全量字體其內存占用對于首包來說可能過大。然而實際在加載過程中用到的文字量有限,可考慮單獨拆分出一個更小的字體專門用于首包。
2. 轉化工具設置:勾選轉換工具面板的“壓縮首包資源”選項,可對首包進行進一步br壓縮。微信還提供了首包資源優化功能,可清理首包中項目并未實際使用的資源,進一步瘦身。
![]()
3. 目標值:一般建議將首包資源大小控制在5MB以下。
2.3 WASM代碼下載與編譯載
代碼包是什么:一般名稱為xxx.webgl.wasm.code.unityweb.wasm.br,默認進行br壓縮。在啟動階段,wasmcode與首包資源并行下載,共同占用下載帶寬;下載完成后進行編譯,編譯過程本身也消耗CPU資源。綜合來看,核心優化方向是降低WASM代碼體積,一般建議將原始代碼包控制在30MB以下。
優化方法:
1. 代碼分包(最常用):在開發者工具中安裝wasmCodeSplit插件,原理是將原WASM拆分為主包(啟動加載)和子包(延遲加載)。小游戲先加載較小的主包進入主場景,再異步加載剩余分包,可大幅降低下載與編譯耗時。具體操作可參考官方文檔。
![]()
2. 代碼裁剪:Unity未針對WebGL平臺做特別裁剪,默認會將引擎、業務代碼、第三方插件全部編譯為WASM二進制。建議勾選“Strip Engine Code”并將“Managed Stripping Level”設為High,可有效縮減代碼包體積。
![]()
3.IL2CPP Size選項:使用Unity 2021以上版本時,可在PlayerSettings中將IL2CPP選項設為更小尺寸(SIZE),減少函數量。
![]()
4.刪除多余插件:排查項目使用的第三方插件,手動移除不必要的Unity模塊(如物理模塊、Unity數據統計等),從源頭減少代碼量。
2.4 引擎初始化與開發者首幀邏輯
該階段做什么:引擎自身模塊與數據初始化、首個場景加載以及MonoBehaviour的Awake/Start流程。此階段CPU處理密集,但網絡處于空閑狀態,可利用預下載功能提前緩存后續資源(詳見第3章資源加載)。
優化要點:
MonoBehaviour腳本在首幀的Start/Awake中應盡量少做邏輯,優先把畫面呈現出來
初始場景不宜過大,通常只呈現Splash場景即可
初始場景中如需加載后續主場景或配置,建議采用分幀策略,切勿在Start/Awake中阻塞
調試工具 —— Android CPU Profiler
微信提供了Android CPU Profiler功能,可用于排查此階段的CPU邏輯開銷。使用方法如下:
1. 從右上角“…”打開菜單 → 開發調試 → 選擇Start CPU Profile開始采集
2. 采集一段時間后點擊Stop CPU Profiler,生成.cpuprofile文件
3. 文件路徑通常為:Android/data/com.tencent.mm/MicroMsg/appbrand/trace
4. 數據可通過Chrome、Edge,或開發者工具中的JavaScript探測器打開分析
注意:若希望在堆棧中看到可讀函數名,需勾選Profiling-funcs或使用Development模式,但會帶來一定性能開銷。否則函數僅顯示數字ID,需借助symbols文件進行映射(微信提供Python替換腳本可自動處理)。此外,若要排查啟動階段,可手動修改game.js,讓游戲在啟動時增加一段黑屏等待時間,便于在啟動期間打開調試。
2.5 啟動耗時檢查清單
首包資源<5MB,僅包含首場景必要資源
WASM原始代碼包<30MB
開啟代碼分包(wasmCodeSplit插件)
勾選“Strip Engine Code”,Managed Stripping Level設為High
Unity 2021 + 設置IL2CPP選項為更小尺寸(SIZE)
首場景只保留Splash,Awake/Start不阻塞主線程
初始場景中后續加載采取分幀策略,不在首幀同步完成
03
資源加載
啟動耗時章節提到首包內容應盡可能精簡,其余資源需放在CDN中延遲加載,因此資源的按需加載是小游戲中至關重要的環節。本章從加載方案選擇、緩存機制、AssetBundle適配三個維度展開,幫助開發者在加載效率與內存占用之間找到平衡。
3.1 常規加載方案
三種方案對比:目前常見的加載策略有三種 —— Addressable方案、AssetBundle方案、Unity Instant Game方案。無論哪種方案,微信小游戲環境均不支持本地Bundle加載,最終都采用上傳CDN方式在游戲運行時異步按需下載。
Addressable(AA):Unity官方推薦的資源管理方案,支持遠程加載與本地緩存,適合大多數項目。
AssetBundle(AB):傳統分包方案,在內存和加載效率方面優于AA,尤其適合相對重度的游戲。目前UWA接觸的小游戲項目中使用AB方案的偏多。
Instant Game:Unity官方提供的自動加載方案,僅適合原生APP未使用資源按需加載、總包體較小的輕度游戲,具體參考團結引擎文檔(https://docs.unity.cn/cn/tuanjiemanual/Manual/Wechat.html)。
對于AA和AB兩種方案,一般可以沿用原APP游戲工程的管理方案,轉化工作量相對較小。后續討論以AssetBundle方案為主,其余方案可參考官方文檔。
AB打包參數建議:使用AssetBundle進行資源打包時,推薦以下設置:
BuildAssetBundleOptions.AppendHashToAssetBundleName:開啟后Bundle名會攜帶Hash,是小游戲中資源緩存及緩存淘汰機制的重要依據(詳見3.2節)。
BuildAssetBundleOptions.ChunkBasedCompression:LZ4壓縮方式,加載速度與包體大小更均衡。
DisableWriteTypeTree:如無新老Unity版本兼容需求,建議開啟以提升加載速度并降低內存。
加載接口:小游戲不支持AssetBundle本地加載,從服務器下載Bundle主要使用以下接口:
UnityWebRequestAssetBundle.GetAssetBundle
UnityWebRequest
不建議使用WWW.LoadFromCacheOrDownload或WWW等帶Cache接口,WebGL模式下會通過JS模擬文件系統,帶來額外內存消耗。
3.2 資源緩存
緩存機制:Unity Loader插件已內置資源緩存與淘汰功能,無需開發者自行實現。UnityWebRequest和UnityWebRequestAssetBundle接口均會自動觸發緩存,業務側無需關心資源是否有緩存,正常調用API即可。可通過Loader插件返回的Log判斷當前是走下載還是緩存。
預下載:Loader插件提供預下載功能,目的是充分利用網絡帶寬。啟動流程中「引擎初始化與首場景準備」階段CPU處理密集,但網絡處于空閑,利用預下載可在此階段提前下載并緩存資源,后續即可直接走本地緩存。預下載文件總體積建議控制在3~5MB以內,文件數量不超過10個(此階段最多10個并發,超出將排隊)。
緩存過濾:并非所有文件都適合緩存。可在導出面板中配置不自動緩存的文件類型(如默認不緩存.json文件),也可在minigame/unity-namespace.js中手動處理緩存邏輯。
版本管理:若資源文件已緩存到本地但后續版本更新,不特殊處理會繼續加載舊緩存。需要以Hash區分資源版本 —— 本地有舊緩存時先清理再寫入新版本。只需將資源URL攜帶Hash作為版本依據即可,默認Hash長度為32;若游戲自行計算CRC,也可設為CRC長度。注意Hash必須以‘-’或‘_’作為分隔符,其他符號無法正確處理版本信息。
緩存上限與淘汰:緩存體積隨游戲進程逐漸增大,默認上限為200MB。可通過 maxStorage調整上限,也可前往微信后臺申請空間提升,最高可達1GB。達到上限后Loader按LRU規則清理早期緩存。為避免頻繁觸發,默認額外多清理30MB(可通過defaultReleaseSize修改)。清理時支持忽略指定文件使其不被自動清理,僅可主動刪除,具體通過minigame/unity-namespace.js中isErasableFile函數控制。
常用接口:
public string WX.PluginCachePath:獲取自動緩存的文件存儲路徑,返回值:${wx.env.USER_DATA_PATH}/__GAME_FILE_CACHE
public string WX.GetCachePath(string url):傳入URL或文件相對路徑,返回緩存路徑(無緩存則返回空字符串)
public void CleanAllFileCache(Action action):清理所有自動緩存的文件
public void CleanFileCache(int fileSize, Action action):從緩存目錄中釋放指定大小的文件
public void RemoveFile(string path, Action action):從緩存目錄中刪除指定文件
3.3 小游戲中的AssetBundle
AB內存差異:AssetBundle打包后的文件由文件頭(屬性數據、Asset信息、依賴關系等)和主數據(平臺對應的Asset數據)兩部分組成。在Standalone、Android、iOS等具有本地文件系統的平臺上,只需加載文件頭,運行時按需讀取資產數據。而WebGL平臺受瀏覽器權限限制不能直接訪問本地IO,加載AB時需將兩部分全部加載到內存中。因此,原生APP只需關注AB文件頭內存,但小游戲還需額外關注AB文件本身的內存占用。
WXAssetBundle方案:小游戲平臺提供了專用AB接口來解決上述問題。以微信小游戲為例,使用WXAssetBundle將文件系統接口橋接到微信的文件系統,使AB可讀寫到小游戲緩存目錄,從而避免AB文件整體進入內存。經測試,使用Unity原生AB接口時,AB文件本體內存會進入UnityHeap并被Reserve;更換為WXAssetBundle后,該內存不再出現在UnityHeap中,而是進入本地緩存(仍需關注默認200MB上限)。
在UWA GOT Online小游戲報告中,也專門統計了WXAssetBundle接口的內存數據。下圖為同一AssetBundle、不同加載接口的對照測試 —— 前半段為WXAB接口加載/卸載,后半段為Unity原生AB接口。可見前者幾乎不會使Reserved Total升高,后者則有明顯變化。
![]()
![]()
打包策略:無論使用哪種AB接口,打包策略都值得關注。分包粒度太大,單個AB下載易卡頓;粒度太小則文件數量過多,IO和下載效率下降。建議根據資源使用頻率和生命周期采用混合策略 —— 常更新的資源(如UI元素)打包至較小Bundle便于熱更新,不常更新的資源(如場景資源)可打包至較大Bundle。通常單個AB包建議控制在10MB以內,總體AB駐留數量在1000個以內。
AB利用率:UWA提出了AB利用率的概念 —— 在特定測試流程中,某AB文件被加載進內存但真正用到的資產(含主動加載與被動依賴)比例很低,則判定為低利用率。這類AB的打包策略可能存在浪費,定位到具體AB后可進一步排查,對包內資產進行更精細的劃分。
3.4 資源加載檢查清單
打包參數:開啟AppendHashToAssetBundleName,使用ChunkBasedCompression (LZ4)
非跨版本兼容需求時,開啟DisableWriteTypeTree
使用UnityWebRequest/UnityWebRequestAssetBundle加載(不用WWW等帶Cache接口)
預下載文件總體積控制在3~5MB,文件數≤10個
單個AB包<10MB,總體AB駐留數量<1000 個
檢查AB利用率,避免大量未使用資產進入內存
關注緩存上限(默認200MB,可申請提升至1GB),必要時調整maxStorage
04
內存
內存是小游戲中與閃退直接掛鉤的核心性能指標,其分布結構、擴容機制和平臺限制均與原生APP存在顯著差異。本章從小游戲進程視角出發,梳理內存的整體構成與各子模塊的優化要點,并對比iOS不同運行模式下的內存分布差異。
4.1 進程視角的內存限制
Android:小游戲通常運行在獨立進程中(如微信小游戲為 com.tencent.mm:appbrand0/1/2),內存相對寬松。一般建議低端機<1.2GB,中高端機<1.5GB。可通過ADB或UWA Gears工具查看進程內存,下圖為Gears采集到小游戲運行時的PSS內存走勢。
![]()
iOS:整體限制更嚴格,建議低端機<1GB,中高端機<1.4GB,最終需結合Xcode Instruments實際數據具體分析,微小通常關注com.apple.WebKit.WebContent進程。
![]()
iOS根據運行模式的不同,內存限制也有所差異:
普通模式(默認):WASM運行無JIT,計算性能受限。小游戲進程與微信綁定,同在WeChat進程中。
高性能模式:CPU算力明顯提升,但內存限制更嚴格 —— 2GB內存設備上限約1GB,3GB及以上設備上限約1.5GB,工程上建議控制在1.2~1.3GB以內。此時小游戲運行在獨立進程com.apple.WebKit.WebContent中。
高性能+模式:在高性能模式基礎上進一步升級,保留游戲獨立進程的同時將渲染重新挪回微信進程,渲染效果和渲染內存消耗均得到改善。推薦使用WebGL2、內存壓力大的游戲開啟該模式。
4.2 小游戲內存結構
Unity WebGL以WebAssembly+WebGL技術為基礎,游戲內存分配完全托管在瀏覽器環境中。適配到小游戲后,進程成為“容器”,內存組成結構基本一致。典型游戲的內存分布如下圖所示:
![]()
各內存區域簡要說明:
UnityHeap:托管堆、本機堆與原生插件底層內存,初始值為轉化面板中的“UnityHeap預留內存”,運行時只增不減且存在內存碎片。這是大部分小游戲項目中最主要的內存來源,是排查與優化的重點。
WASM編譯:代碼編譯與運行時指令優化產生的內存,占比相對較高。一般無工具直接檢測該部分精確占用。常見經驗上可按未壓縮WASM代碼包的約8~12倍進行數量級估算,但實際比例受平臺內核(iOS/Android)、是否開啟JIT、分包策略及編譯優化等級影響較大,以真機實測為準。
GPU內存:紋理或模型Upload GPU后的顯存占用。壓縮紋理格式支持受宿主、瀏覽器、設備GPU和轉換方案影響,PC開發者工具中可能回退為RGBA32,必須以真機實測為準。
音頻:Unity將音頻傳遞給容器后,播放時占用的內存。
基礎庫+Canvas:小游戲公共庫、Canvas畫布等固定開銷,基本無法針對性優化。
其它:Emscripten文件系統模擬等相關占用。
4.2.1 UnityHeap
擴容機制:UnityHeap是為實際使用的內存部分提前預留的上限,實際占用指標為DynamicMemory。當DynamicMemory接近預留上限時(如初始500MB,達到約490MB以上),會觸發擴容 —— 在內存中新分配一個更大的堆空間(如550MB),將舊內容復制過來后再釋放舊堆。短時間內產生內存塊復制導致的內存尖峰,極易造成閃退,尤其在iOS上更為嚴重。
初始值設定:不宜過小(頻繁擴容導致尖峰),也不宜過高(≥1024MB直接導致啟動失敗)。官方參考值:輕度游戲(休閑類)256MB,中度游戲(模擬經營、卡牌成長)496MB,重度游戲(SLG、MMO)768MB。
DynamicMemory構成:DynamicMemory=MonoHeap+NativeReserved+原生插件內存,需分別關注各部分走勢,避免泄漏。
Mono堆內存是C# 腳本托管對象(string、List、自定義類實例、委托等)的核心內存區域。GC會正常回收對象,Mono使用量可以有增有降;但受WebAssembly內存沙箱機制影響,堆容量一旦被峰值撐高便難以回縮,表現為峰值保持。因此在小游戲上要特別關注Mono的分配,避免單幀內出現大量分配把峰值頂上去。
NativeReserved由Unity Native產生,屬于引擎內部對象。動畫、字體、Shader等資源類內存均包含在此。紋理、網格等傳給GPU的資源原本計入GPU內存,但若開啟了Read/Write Enable選項,CPU端還會額外存儲一份,計入NativeReserved。此外,使用Unity原生AssetBundle接口時AB文件本體內存也會進入NativeReserved,使用WXAssetBundle可將其轉移至本地緩存。
從WXAB和原生AB的加載對比測試數據中可以看到:使用原生AB接口會使NativeReserved明顯上升,連帶推高DynamicMemory;即使卸載后NativeReserved回落,DynamicMemory也已被撐高,剩余空間相當于被預留住了。
![]()
針對Native部分的排查可使用Unity Memory Profiler工具,需開啟Development Build及AutoConnect。
第三方原生插件內存(如Lua)通常沒有單獨數值展示,可觀察DynamicMemory與Mono、Native部分的差值是否偏大。若對插件管理較明確,可通過對比測試(開關插件觀察內存變化)進一步定位來源。
4.2.2 WASM編譯
小游戲啟動時下載WASM代碼包并進行編譯,編譯后占據的內存很大。常見經驗上可按未壓縮WASM代碼包的約8~12倍進行數量級估算(如微信官方數據:iOS上30MB未壓縮代碼約需300MB運行時編譯內存),但實際比例受平臺內核、是否開啟JIT、分包策略及編譯優化等級影響較大,以真機實測為準。
最常見有效的優化方案是代碼分包 —— 將原WASM代碼包拆分為主包和子包:
主包(wasmcode):啟動時首先加載,大小約為原包的1/3~1/2,建議控制在3~5MB,需盡量覆蓋大多數游戲場景和流程。
子包(wasmcode1/wasmcode2):分別對應Android和iOS平臺,約7~15MB,在游戲運行一段時間后自動加載或缺失函數時再加載。
分包后總大小可能比原包更大(因多出復制文件),但只有主包一定加載,其余延遲或按需下載,對運行幾乎無影響。
4.2.3 常見資源類型內存
對于Unity開發者而言,更直觀、更容易上手修改的是紋理、網格、動畫、字體等常見資源類型。各類資源在小游戲內存中的歸屬如下:
GPU內存:紋理、網格、RenderTexture
NativeReserved(UnityHeap內):動畫、字體、Shader、開啟了Read/Write的紋理/網格、使用原生AB接口的AB文件本體
獨立內存:音頻資源
《Unity移動端游戲性能優化簡譜》中對各類資源內存優化已有系統介紹,以下僅提煉各類型的核心優化要點:
紋理:內存大戶,重點關注三個方面 —— ①格式:避免RGBA32/ARGB32等未壓縮格式,優先使用ASTC/ETC2等移動端壓縮格式(注意小游戲中格式支持因宿主和真機而異,務必真機驗證);②分辨率:1024以上的紋理需評估是否必要,高分辨率在小屏幕上往往看不出差異卻占4倍內存;③Read/Write Enabled:開啟后CPU端額外存一份,計入NativeReserved,非必要一律關閉。此外,開啟Mipmap會使紋理內存增至約1.33倍,需權衡帶寬收益后決定。
網格:關注頂點數和面片數是否超出實際表現需求,頂點屬性(UV2、Color、Tangent等)在不必要時可剝離以減小數據量。網格同樣存在Read/Write Enabled問題,開啟后CPU端多存一份。
動畫:動畫Clip本身占用內存,且播放時涉及骨骼Transform更新等CPU開銷。關注Animation Clip數量與時長,適當壓縮關鍵幀或降低采樣率。大量Animator處于Active狀態還會額外增加CPU負擔。
音頻:小游戲中音頻內存獨立于UnityHeap,但也需關注。建議使用壓縮格式(如MP3/Vorbis),并根據音頻類型選擇Loader(Decompress On Load會將完整數據常駐內存,Streaming則按需讀取但CPU開銷更高)。
字體:使用TextMeshPro時,SDF Atlas以Alpha8格式存儲,內存較低;但若使用動態字體(.ttf直接渲染),會產生較大內存占用。建議優先使用TMP靜態字體方案。首包中使用的字體應盡可能精簡字符集。
粒子系統:粒子數量、發射器復雜度直接影響CPU端計算和內存分配,中低端機上應限制Max Particles并精簡粒子層級。
以上資源內容可以在UWA GOT Online小游戲報告中進行詳細排查,在Resource模式中可獲取具體的資源列表。
![]()
![]()
注意:新上手小游戲平臺的開發者常遇到紋理內存遠超預期的現象 —— 明明做了紋理壓縮,測試數據卻顯示很高。這通常是PC開發者工具中ASTC等壓縮格式回退為RGBA32所致,紋理壓縮格式務必以真機測試為準(詳見1.5節關于PC工具與真機測試的原則說明)。
4.3 iOS高性能與高性能+模式對比
iOS的高性能模式和高性能+模式中內存分布存在明顯差異,UWA通過對比測試幫助讀者形成更直觀的認識。
高性能模式:小游戲運行在獨立進程com.apple.WebKit.WebContent中。幾乎所有類型的內存都會進入WebContent進程(不會使WeChat進程內存上升),僅音頻資源例外。真正導致閃退的進程是WebContent進程。內存分布大致如下:
![]()
高性能+模式:官方描述為“開創性地在保留游戲獨立進程的基礎上將渲染重新挪回了微信進程”。測試結果顯示,屬于GPU的紋理、網格、RT不再進入WebContent進程,而是進入WeChat進程。但開啟了Read/Write的紋理/網格在CPU端仍有一份內存位于NativeReserved中,屬于WebContent進程。
再次驗證,小游戲閃退只與WebContent進程有關,一般建議控制在1.3GB以內;WeChat進程即使大幅超過這個范圍也不會閃退。內存分布如下:
![]()
可見高性能+模式的內存占用相比高性能模式確有下降,若高性能模式下內存吃緊可考慮開啟高性能+。但具體仍需自行評估客戶端版本、是否存在新的畫面或性能問題。
4.4 內存檢查清單
UnityHeap初始值合理設置(參考:輕度256MB/中度496MB/重度768MB,避免<256MB或≥1024MB)
關注DynamicMemory走勢,避免泄漏和頻繁擴容引發的內存尖峰
Mono堆避免單幀內大量分配
紋理避免開啟Read/Write Enable(否則CPU端多存一份計入NativeReserved)
考慮使用WXAssetBundle代替原生AssetBundle接口,避免AB本體進入UnityHeap
WASM代碼包按約8~12倍體積數量級估算編譯期內存,以真機為準,關注分包效果
紋理壓縮格式以真機測試為準,勿依賴PC開發者工具數據
內存壓力大的游戲考慮開啟iOS高性能+模式
05
CPU壓力
受限于WASM虛擬機的執行效率以及常規C# 多線程/Job多線程能力受限,小游戲上CPU性能約為原生APP的1/3(實際比例因機型、Unity版本、iOS模式、瓶頸類型等因素而異),是小游戲平臺上最易出現高壓問題的維度。本章聚焦小游戲中獨有或更容易放大的CPU瓶頸場景,通用排查思路可參考《Unity移動端游戲性能優化簡譜》中的CPU相關章節。
5.1 蒙皮動畫
小游戲中較常見的CPU性能壓力來自蒙皮動畫(Skinned Mesh Renderer)。在原生APP上,蒙皮計算可在工作線程處理,對主線程影響較小;而在小游戲中常規多線程能力受限,MeshSkinning.Update的耗時直接影響主線程,不經處理可能成為瓶頸。下圖為某款SLG項目在Mi 10上的運行數據,在小怪較少的戰斗場景中也會有持續4ms的MeshSkinning.Update開銷,相對偏高。
![]()
優化方向有兩個:
GPU Skinning:將蒙皮計算從CPU移至GPU處理。團結引擎已內置該功能,也可使用第三方GPU Skinning方案。上述SLG場景中替換為GPU Skinning方案后,在小怪數量更多、更復雜的戰斗場景開銷也僅有1~2ms,且表現無明顯差別。
控制蒙皮規模:進一步限制骨骼數量和頂點數量,適當犧牲表現精度以換取性能。
![]()
5.2 邏輯代碼
部分在APP上運行穩定的計算邏輯,直接移植到小游戲中效率可能明顯偏低,需要適量精簡。這一部分與項目業務邏輯強相關,難以給出統一方案,但可遵循以下排查與優化思路:
配合Profiler打點定位耗時的函數,優先處理耗時最高的熱點
關注每幀都在執行的邏輯(Update/FixedUpdate),檢查是否有不必要的重復計算
WebGL中Lua不支持JIT,避免將Lua用于重度計算邏輯
回顧1.5節中關于PC工具與真機測試的原則:PC端CPU性能遠超真機,務必以真機Profiler數據為準
5.3 UI模塊
UGUI是許多Unity項目中CPU開銷的重要來源,在小游戲單線程環境下影響更為明顯。主要關注以下幾個高耗時函數:
EventSystem.Update:UGUI事件系統的每幀更新,耗時偏高時通常與兩個因素有關 —— 一是輸入回調中掛載了耗時過高的邏輯函數,可通過Profiler打點進一步定位;二是大量UI元素默認開啟了Raycast Target選項,而實際上多數Image、Text并不需要響應點擊事件,關閉該選項可直接降低事件遍歷開銷。
Canvas.SendWillRenderCanvases:Canvas的渲染重建入口,當UI元素的Transform、顏色、層級等發生變化時觸發。該函數耗時與發生變化的UI元素數量成正比。優化方向包括:將動態UI和靜態UI拆分到不同Canvas中,避免靜態元素被頻繁連帶重建;減少不必要的Layout Group、Content Size Fitter等自動布局組件使用。
Canvas.BuildBatch:UI元素合批為Mesh的主要耗時點,通常緊隨SendWillRenderCanvases之后。合并批次時若發生打斷(如同一Canvas下使用了不同材質/圖集),會顯著增加重建耗時。規劃好UI的圖集和材質共用策略、保持合批連續性,可有效降低該開銷。
CanvasRenderer.SyncTransform:當UI元素的Transform發生變更時觸發,頻繁調用時會連帶導致渲染更新開銷增高。需注意某些動畫或邏輯中對UI元素Transform的頻繁修改。
通用建議:動態/靜態UI分離到不同Canvas、關閉不需要的Raycast Target、減少自動布局組件使用、謹慎使用UI Particle(會作為UI元素高頻更新并重建Mesh)。
5.4 實例化與銷毀
在游戲運行過程中,頻繁的Instantiate和Destroy操作在小游戲上開銷明顯,原因在于單線程環境下內存分配與對象初始化的耗時直接阻塞主線程。
場景/模塊切換:進入新場景或新界面時,建議采用分幀加載策略,避免在一幀內完成大量對象的Instantiate導致明顯卡頓。可結合對象池(Object Pool)復用高頻創建銷毀的對象(如子彈、特效、UI列表項),減少頻繁的內存分配與回收。
Activate/Deactivate vs Instantiate/Destroy:對于需要頻繁顯隱的對象,使用SetActive的代價遠低于Instantiate/Destroy,但大量同時處于Active狀態的對象仍會增加Update等邏輯開銷,需要在兩者之間平衡。
Resources.UnloadUnusedAssets:卸載未使用資源是一項耗時操作,建議在場景切換完成后的靜默期執行,避免在游戲流程中頻繁調用。
5.5 CPU壓力檢查清單
關注SkinnedMeshRenderer數量和MeshSkinning.Update耗時
考慮使用GPU Skinning(團結引擎已支持)或第三方方案
控制蒙皮骨骼數和頂點數,適當犧牲精度換取性能
邏輯代碼配合打點定位高耗時函數,針對性精簡
WebGL中避免用Lua處理重度邏輯(不支持JIT)
UI:動態/靜態分離到不同Canvas,關閉非必要的Raycast Target
UI:減少自動布局組件使用,規劃好圖集合批策略
頻繁創建銷毀的對象使用對象池,場景切換采用分幀加載
06
GPU壓力
小游戲上GPU運行效率與APP接近,相對于CPU較不容易成為性能瓶頸,但GPU壓力同樣關系到幀率、發熱與功耗。本章提煉《Unity移動端游戲性能優化簡譜》中GPU章節的核心要點,并補充微信小游戲平臺在iOS上經驗證的幾個GPU壓力關鍵來源(HDR、RT使用、紋理精度等)。
注意:1.5節中關于PC開發者工具與真機測試的原則在GPU側尤為重要 —— ASTC等壓縮紋理格式在PC上可能回退為RGBA32,WebGL 2.0特性(GPU Instancing、SRP Batcher)在PC與真機上的表現也可能存在差異,GPU數據務必以真機實測為準。
6.1 判斷GPU壓力
當整體幀耗時明顯超出目標幀,但CPU側各模塊耗時之和與之存在較大差距時,通常傾向于判定存在GPU壓力。在原生APP上可借助GPU Clocks或同步等待函數標記輔助判斷,但小游戲環境下渲染線程模型存在差異,更建議結合實際表現綜合評估 —— 例如CPU耗時已優化到位而幀率仍不達標、或降低渲染分辨率后幀率明顯回升,均可作為GPU Bound的佐證。
6.2 頂點階段壓力
頂點階段壓力取決于渲染面片數與頂點著色器復雜度。GPU Primitive參數中,若剔除圖元占比高達70-80%,說明存在大量浪費,需排查模型制作是否合理、大模型是否未拆小導致整體提交GPU、網格是否過于精細而屏占比有限等。
優化方法包括:LOD分級控制遠距離面片數、精簡復雜模型、CPU端提前剔除不可見物體、在中低端機上關閉多光源/陰影/多Pass等使面片數翻倍的特性。
6.3 片元階段壓力
片元階段的計算量取決于每幀繪制的像素數,由渲染分辨率和Overdraw共同決定。
渲染分辨率:分級控制分辨率是最實用的GPU優化手段。低端機降至0.7-0.8倍,像素數可減半,3D場景通常可接受輕微模糊。需注意實際渲染分辨率與設備DPR(Device Pixel Ratio)相關 —— 高DPR屏幕即使設置較低倍率仍可能產生高像素負擔,建議按像素總數而非固定比例制定分檔標準。URP項目中可利用Camera Stack將3D場景渲染到低分辨率RT中,避免UI同時變模糊。
Overdraw:指像素被重復繪制的次數。不透明物體應通過Render Queue調整渲染順序來控制;粒子系統和UI是半透明Overdraw的主要來源 —— 中低端機精簡粒子層級、限制Max Particles、裁剪純透明貼圖面積、考慮動畫幀烘焙替代;UI側注意全屏遮擋時關閉底層、Mask替換為RectMask2D等。此外,后處理、Blit、Copy等操作同樣會產生全屏級別的Overdraw。特別地,開啟HDR會引入額外的全分辨率RT拷貝操作以及額外的顯存開銷,在小游戲環境下對GPU壓力影響顯著,建議謹慎評估是否必要。
6.4 著色器與后處理
著色器:優先關注屏占比較高、Shader相對復雜的渲染對象(地表、建筑、特效),而非屏占比低的角色。項目中“萬能Shader”常導致GPU對大量未使用特性進行全額計算,應通過關鍵字開關或拆分Shader優化。
后處理:是GPU壓力的常見來源 —— Bloom中低端機可從1/4分辨率開始下采樣;SMAA開銷較高不建議在中低端使用;DOF移動端開銷大需謹慎;盡量使用Local Volume按需開啟而非全局常駐。
6.5 GPU帶寬
GPU帶寬主要影響能耗發熱而非幀率。Mali官方數據顯示約1GB/s帶寬造成80-100mW功率開銷,在游戲總功率中占比可觀。在小游戲中,渲染產生的GPU帶寬開銷在DRAM帶寬中占絕大部分,主要來自:頻繁的RT使用與切換、高精度紋理資源的采樣、以及HDR或后處理引入的額外RT拷貝。
優化手段包括:盡量復用RT減少切換、使用合理壓縮格式降低單像素傳輸量、3D場景務必開啟Mipmap(改善效果遠超紋理壓縮)、避免全局強制各向異性過濾、減少不必要的Copy和后處理采樣。
6.6 GPU壓力檢查清單
關注渲染面片數,排查剔除圖元占比過高的場景
按像素總數分檔控制渲染分辨率,中低端機可降至0.7-0.8倍
排查Overdraw(粒子系統、UI、后處理),定位熱點資源
關注屏占比高且Shader復雜的渲染對象,拆分“萬能Shader”
后處理效果分級取舍,中低端機關閉開銷較高的效果
關注GPU帶寬 —— 紋理壓縮、Mipmap、減少不必要的Copy和采樣
GPU數據(紋理格式、WebGL 2.0特性等)務必以真機測試為準
07
功耗優化
小游戲的功耗優化整體上可參考移動端的優化思路與經驗,且由于移動設備散熱條件有限,功耗問題在小游戲上同樣不容忽視。功耗不僅關乎設備續航,更與發熱直接掛鉤 —— 當設備溫度超過系統溫控閾值后,會觸發CPU/GPU降頻,導致幀率驟降,形成“高功耗→發熱→降頻→卡頓”的惡性循環。因此功耗優化本質上也是性能穩定性優化,核心手段與CPU、GPU章節的優化思路一脈相承。
7.1 幀率對功耗的影響
首先要明確一個基本規律:在其它條件不變的情況下,幀率與功耗近似成正比,60FPS的功耗約為30FPS的兩倍。這意味著高端機型上若不加限制地跑滿幀率,反而可能因為功耗過高導致發熱更嚴重。建議根據項目類型制定合理的目標幀率(如休閑游戲30FPS,中度游戲30~60FPS),也可對不同場景動態調整 —— 例如主界面、掛機場景降低幀率,核心戰斗場景再恢復到目標幀率,從而有效控制整局功耗。
7.2 功耗的來源
小游戲運行時的功耗主要來自以下幾個方面:
CPU:邏輯計算、蒙皮、動畫、物理、腳本等,是小游戲功耗最主要的來源。
GPU:頂點處理、片元著色、紋理采樣、帶寬傳輸等。
屏幕顯示:屏幕亮度與分辨率直接關聯功耗,但開發者通常無法直接控制。
網絡傳輸:持續的網絡請求與數據收發也會產生額外功耗。
對于開發者而言,能夠直接著手優化的主要是CPU與GPU兩部分。
7.2.1 CPU側功耗優化
在原生APP上,通常需要分別排查CPU主線程與子線程中的壓力來源。但小游戲環境下常規多線程能力受限,因此重點關注CPU主線程上各模塊的耗時即可。排查思路與本文「CPU壓力」章節一致:通過Profiler定位高耗時模塊(渲染、UI、物理、動畫、邏輯腳本等),找到項目中壓力較大的模塊并予以針對性優化。由于功耗與CPU占用正相關,降低主線程耗時本身就是在降低功耗。
7.2.2 GPU側功耗優化
GPU側功耗主要從GPU計算壓力與GPU帶寬兩個維度進行排查,與本文「GPU壓力」章節的思路一致:控制同屏面片數、合理設置渲染分辨率、減少Overdraw、精簡Shader復雜度,同時關注紋理采樣次數與帶寬占用。降低GPU負載同樣直接降低功耗。
7.3 功耗優化檢查清單
根據項目類型制定合理的目標幀率,避免高端機無限制跑滿
對不同場景動態調整幀率(主界面/掛機降幀,核心玩法恢復目標幀率)
通過CPU主線程Profiler定位高耗時模塊并針對性優化
從GPU計算壓力與GPU帶寬兩個維度排查GPU側功耗
將高溫環境(戶外、充電時游玩)納入測試覆蓋范圍
iOS高性能模式下尤其關注啟動階段的發熱,避免長時間連續高負載
08
APP轉小游戲經驗分享:同步轉異步功耗優化
本章分享一個實際項目中將APP遷移到小游戲時遇到的典型問題 —— AssetBundle同步加載的兼容性改造,以及我們在實戰中摸索出的一套低成本解決方案。
8.1 問題背景
在原生APP項目中,同步加載與異步加載在實際運行時的體驗差異通常不大,開發團隊在早期也不一定會嚴格區分兩者的使用場景。但在小游戲環境中,AssetBundle文件本身不支持同步加載(從已加載的AssetBundle中加載具體資源仍可使用同步接口),這意味著原本在APP中大量使用的同步加載邏輯都需要逐一改造。如果項目在原生APP階段沒有提前做好這方面的規范和管理,后續轉換時的工作量會相當可觀。
8.2 快速定位方法
在Unity編輯器中將小游戲工具面板的Play Mode切換為Web Play Mode,此時在Editor中運行游戲,一旦觸發AssetBundle的同步加載就會直接拋出WebGL platform not support sync load method這類報錯,幫助快速發現哪些加載鏈路存在問題。
![]()
![]()
進一步地,還可以在同步加載接口的報錯位置額外增加一條報空日志,顯式輸出對應資源的路徑信息,這樣每次報錯時就能直接看到是哪個AB文件在哪個時機被同步加載了,排查效率大幅提升,不必逐個翻看代碼調用鏈。
![]()
8.3 集中預加載方案
從已加載的AssetBundle中加載具體資源仍然可以使用同步接口。基于這一點,核心思路就清晰了:只要在使用同步接口加載資源之前,確保對應的AssetBundle已經通過異步方式加載到內存中且未被卸載,那么原來的同步資源加載邏輯就完全可以保持不變。
按照這個思路,我們的做法是在游戲流程中找到一個統一的、早于所有實際資源加載的時機點,把所有會被同步加載使用的AssetBundle提前用異步接口統一加載一遍。只要保證后續真正使用同步接口加載資源時這些AB仍在內存中且未被卸載,整個項目中原有的同步加載邏輯就不需要逐一修改,也不會在小游戲環境中觸發報錯。
例如原先在Initialize階段使用了同步加載會導致報錯,我們找到了一個更早的時機點,通過PreloadAsync先用異步接口集中加載了timeConfig在內的多個后續會用同步加載的資源,那么后續的邏輯無需修改也不會報錯了。
![]()
![]()
這種方式本質上是將“同步改異步”的改造工作從散落在各處的資源加載點集中收攏到了一個統一的預加載節點,既降低了修改工作量,也避免了逐處改造時容易遺漏或引入新問題的風險。
09
結束語
這篇文章之所以稱為簡譜,實在是因為這些筆墨遠不能達到面面俱到,很多內容還未涉及到,或者限于篇幅和重點不能深入討論。它更多的是立足于如何以用好一套完善完整的性能工具為基礎,構建發現問題-解決問題-監控問題的優化思維和優化體系,使得性能優化的工作事半功倍。更多的優秀內容,歡迎在UWA社區中進行搜索。
參考
《Unity移動端游戲性能優化簡譜》
https://edu.uwa4d.com/lesson-detail/430
本文內容就介紹到這里啦,更多內容可以前往UWA學堂進行閱讀。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.