遊戲 Lag 白皮書 › L1 用戶端遊戲程式
用戶端垃圾回收 Client GC (Unity C#, Unreal, Lua)
原因 ID cg-gc · 主要負責 遊戲開發團隊(用戶端開發)
在含圖解與實驗的完整版中開啟卡片 →
回收用過即丟的記憶體(垃圾)期間,整個遊戲會暫停。特徵是以規律的間隔出現卡頓。
為什麼 每個畫格都建立再丟棄暫時性的字串、陣列、List → 於是 垃圾累積起來後,GC 暫停主執行緒進行回收 → 畫面上 每隔幾秒~幾十秒規律地出現卡頓
- 症狀
- 卡頓, 定格
- 因素
- 停滯
- 誰會遇到
- 只有我
- 何時
- 固定週期, 人潮湧入時
- 負責單位
- 主要負責 遊戲開發團隊(用戶端開發)
- 遊戲開發團隊要做的事
- 減少記憶體配置(避免字串串接、LINQ、lambda 捕捉)、使用物件池、開啟漸進式 GC(Unity 2020 以後為預設值)、趁讀取畫面這類暫停也無妨的時機先執行 GC。
- 數值參考
- 一般每次數 ms~100ms,低階手機或記憶體用量大的遊戲會更久(實驗中約 150~170ms)。Unity 的 GC 每次執行都會檢查整個 heap,所以遊戲使用中的記憶體越多,暫停就越久。
- 圖表上
- 固定週期飆高 · 畫格時間、GC 執行時間點
- 查看位置
- 在開發版本中查看 Unity Profiler 的 GC.Collect、GC.Alloc 標記。Unreal 則看 stat GC 與 stat Hitches(把超過 t.HitchFrameTimeThreshold 所設時間的畫格記進 log)
- 符合的跡象
- 每個飆高的畫格都有 GC.Collect 區段,長度與飆高的長度相近,間隔為幾秒~幾十秒且規律。在人多的地方,每個畫格的 GC.Alloc 會增加
- 不符合的跡象
- 飆高的畫格沒有 GC 區段時,是「主執行緒同步載入、著色器編譯」或「大量角色同畫面的渲染負載」
- 確認方式
- 需要遊戲伺服器/用戶端的 log 與指標
- 深入了解
- 在 Unity 這類使用 C# 的用戶端很常見。主要元凶是每個畫格都重新產生戰鬥紀錄字串、傷害數字、UI 文字的程式碼。如果只在人多的地方卡頓,代表有程式碼產生的垃圾會隨人數成比例增加。漸進式 GC 會把回收工作分散到每個畫格各做一點(Unity 預設 3ms),但產生垃圾的速度一旦快過回收速度,最後還是會一次暫停。Unreal Engine 也有自己的 GC,負責清理不再使用的遊戲物件;雖然依引擎版本與設定而不同,預設設定下約每 1 分鐘執行一次,有時會造成每隔 1 分鐘頓一下。遊戲規則用 Lua 等腳本撰寫的用戶端,該腳本的 GC 也會另外執行。
出處
- Garbage collection modes Unity
漸進式 GC 為預設值,會分散到多個畫格回收;關閉時,檢查整個 heap 的期間主執行緒會暫停,可能長達數百 ms - Scripting.GarbageCollector.CollectIncremental Unity
漸進式 GC 每次使用的時間(時間片段)預設目標為 3ms(incrementalTimeSliceNanoseconds) - Garbage Collection Settings in the Unreal Engine Project Settings Epic Games
Unreal GC 依固定間隔(Time Between Purging Pending Kill Objects,單位為秒)執行的設定。這份文件沒有寫出預設值 - Profiler markers reference Unity
GC.Collect:垃圾回收期間程式碼暫停的區段(不到 1ms~數百 ms);GC.Alloc:managed heap 配置 - Stat Commands in Unreal Engine Epic Games
stat GC(垃圾回收統計)、stat Hitches(把超過 t.HitchFrameTimeThreshold 的畫格記錄到 log)
相關原因
同一層:L1 用戶端遊戲程式
同一症狀(卡頓)在其他層的原因
查看含圖解與實驗的完整版卡片