Incremental GC có phải liều thuốc đặc trị giúp game hết giật lag?
Minh Khoa
Tác giả
Game thỉnh thoảng khựng một nhịp. Mở Profiler thấy GC spike, bật Incremental GC rồi nghĩ: “Ổn, giải quyết xong.”
Nhưng nếu code vẫn liên tục tạo bộ nhớ tạm, công việc dọn dẹp vẫn còn nguyên.
GC — Garbage Collector — thu hồi bộ nhớ của những object C# không còn được sử dụng. Incremental GC giúp chia công việc này qua nhiều frame, giảm những lần dừng kéo dài. Nó vẫn tốn thời gian CPU và không xử lý được những cú giật do GPU, physics hay tải tài nguyên.
Hình dung bằng một ví dụ
Để giữ 60 FPS, mỗi frame có khoảng 16,7 ms.
Giả sử phần việc bình thường mất 10 ms, một đợt GC cần thêm 8 ms:
Dọn dồn một lần:
10 ms → 18 ms → 10 ms → 10 ms
↑ Vượt ngân sách frame
Chia nhỏ công việc:
12 ms → 12 ms → 12 ms → 12 ms
Đây là số minh họa. Thực tế, GC không bảo đảm chia đều hay chỉ mất đúng 2–3 ms mỗi frame. Unity có thể tận dụng thời gian chờ cuối frame khi dùng VSync hoặc Application.targetFrameRate. Unity Manual
Bật rồi, vì sao vẫn khựng?
Nếu frame vốn đã mất 16 ms, thêm một lát GC vẫn có thể khiến nó trễ hạn.
Ngoài ra, game tiếp tục thay đổi object trong lúc GC làm việc. Unity dùng write barrier để theo dõi thay đổi tham chiếu, và có thể phải quét lại những object liên quan. Nếu thay đổi quá nhiều khiến quá trình đánh dấu không hoàn tất, GC có thể quay về một lượt thu gom đầy đủ, không incremental. Cơ chế này đã được Unity mô tả chính thức. Unity Manual
Một chỗ rất dễ tạo rác: cập nhật UI mỗi frame
void Update()
{
scoreText.text = "Score: " + score;
}
Điểm số chưa đổi nhưng code vẫn liên tục tạo chuỗi.
Cách sửa đơn giản là cập nhật khi điểm thay đổi:
public void AddScore(int amount)
{
score += amount;
scoreText.text = "Score: " + score;
}
Vẫn có allocation khi tạo chuỗi, nhưng số lần giảm đáng kể. Cùng cách nghĩ đó, mình tái sử dụng List, buffer và pool cho những object xuất hiện thường xuyên. Unity khuyến nghị giảm allocation lặp lại về gần 0 byte mỗi frame ở các đoạn chạy thường xuyên. GC best practices
Còn RAM thì sao?
GC của Unity không di chuyển object để gom các khoảng trống lại. Vì vậy, bật Incremental GC không giải quyết phân mảnh bộ nhớ.