Trong lúc server Java hoặc C# dừng mọi thread để thu gom rác (stop-the-world), toàn bộ server đứng lại.
Vì sao Heap đầy nên GC bắt đầu chạy → Dẫn đến Dừng mọi thread game để thu gom (càng nhiều dữ liệu còn sống thì càng lâu) → Trên màn hình Mọi người chơi trên server cùng lúc đứng hình rồi tua nhanh
Phụ trách chính Phát triển server (Đội phát triển game) · Phối hợp Hạ tầng server (Đội hạ tầng)
Việc cần làm (Đội phát triển game)
Chỉ định rõ trong tùy chọn khởi chạy một GC có thời gian dừng ngắn (ZGC, Shenandoah, hoặc G1 với mục tiêu thời gian dừng thấp hơn), giảm cấp phát bộ nhớ, điều chỉnh kích thước heap.
Việc cần làm (Đội hạ tầng)
Dùng instance đủ bộ nhớ để cấp heap rộng rãi, cấp cho container từ 2 CPU và khoảng 1,8 GB bộ nhớ trở lên (với JDK 26 trở xuống, nếu ít hơn mức này thì Serial GC được chọn làm mặc định), giám sát thời gian GC pause.
Con số tham khảo
Minor GC (chỉ thu gom đối tượng mới ở vùng Young) mất vài ms đến vài chục ms. Full GC, thu gom toàn bộ heap chứa vài GB dữ liệu còn sống, có khi mất hơn 1 giây. ZGC dừng dưới 1 ms gần như bất kể kích thước heap, còn Shenandoah cũng dừng ngắn vì thời gian dừng không tăng theo kích thước heap.
Trên đồ thị
Vọt lên theo chu kỳ · Thời gian xử lý tick của server, thời gian GC pause
Chỗ cần xem
Bật log GC, đặt thời điểm và độ dài của mỗi lần dừng chồng lên đồ thị thời gian xử lý tick của server. Java: dòng Pause trong log do tùy chọn khởi chạy -Xlog:gc* ghi ra (JDK 8 trở xuống dùng -XX:+PrintGCDetails); .NET: chỉ số GC pause trong dotnet-counters (.NET 9 trở đi là dotnet.gc.pause.time, 8 trở xuống là % Time in GC since last GC); Go: dòng mà GODEBUG=gctrace=1 ghi ra sau mỗi lần GC
Đúng nếu
thời điểm tick vọt lên trùng với thời điểm GC pause, độ dài lần dừng xấp xỉ độ dài đợt vọt. Mọi zone và kênh trên server cùng lúc vọt lên
Loại trừ nếu
log GC không có lần dừng dài nào mà tick vẫn vọt lên → nguyên nhân khác như lock, gọi đồng bộ (blocking), ghi ổ đĩa. Chỉ một zone vọt lên → GC của script engine (mem-script-gc) hoặc tải của zone đó
Cách kiểm tra
Kiểm tra bằng công cụ hạ tầng (không cần code game)
Tìm hiểu thêm
Với G1 của Java, mục tiêu cho mỗi lần dừng mặc định là 200 ms, tương đương 4 tick trên server 20 tick. Nếu container được cấp dưới 2 CPU hoặc dưới khoảng 1,8 GB bộ nhớ, Java từ JDK 26 trở xuống sẽ chọn Serial GC (thu gom bằng một thread duy nhất) làm GC mặc định, nên thời gian dừng dài hơn nhiều. Server C# (.NET) thường bật server GC và background GC, nhưng việc thu gom thế hệ 0 và 1 (Gen0·1, nơi chứa đối tượng mới) và Full GC có kèm compaction (dồn gọn bộ nhớ) vẫn dừng mọi thread. Go thường chỉ dừng dưới 1 ms, nhưng khi cấp phát nhiều, bên xin bộ nhớ phải gánh một phần việc GC nên tick chậm đi. Dù là cơ chế nào, nếu cấp phát nhanh hơn thu gom thì cuối cùng thread game vẫn bị dừng. G1 chuyển sang Full GC, còn ZGC dừng thread đang xin bộ nhớ cho đến khi thu gom xong.
Garbage-First (G1) Garbage CollectorOracle Mục tiêu dừng mặc định của G1 là 200 ms (MaxGCPauseMillis); nếu hết bộ nhớ trong lúc thu gom thì chuyển sang Full GC, tức dừng tất cả để compact toàn bộ heap
JEP 439: Generational ZGCOpenJDK ZGC dừng không quá 1 ms và không phụ thuộc kích thước heap, G1 dừng từ vài ms đến vài giây. Nếu cấp phát nhanh hơn thu hồi thì có nguy cơ allocation stall (dừng do cấp phát)
Background garbage collection.NET Background GC chỉ áp dụng cho thu gom thế hệ 2, còn thu gom thế hệ 0 và 1 (foreground GC) dừng toàn bộ managed thread
A Guide to the Go Garbage CollectorGo GC của Go phần lớn chạy đồng thời (concurrent) với chương trình, chỉ có những lần dừng toàn bộ rất ngắn; khi cấp phát nhiều, goroutine phải gánh bớt việc GC (assist) nên phát sinh độ trễ
JEP 271: Unified GC LoggingOpenJDK Từ JDK 9, log GC được viết lại trên cơ chế log hợp nhất (-Xlog); -Xlog:gc ghi một dòng cho mỗi lần GC, giống -XX:+PrintGC trước đây
The java CommandOracle Bảng chuyển các tùy chọn log GC cũ sang -Xlog: -XX:+PrintGCDetails tương ứng với -Xlog:gc*
dotnet-counters diagnostic tool.NET .NET 9 trở đi hiển thị bằng các meter System.Runtime (dotnet.gc.pause.time, v.v.), .NET 8 trở xuống hiển thị bằng EventCounter cũ (% Time in GC since last GC, v.v.)
runtime packageGo GODEBUG=gctrace=1: mỗi lần GC một dòng, gồm wall clock time của từng giai đoạn, kích thước heap lúc GC bắt đầu và kết thúc, và heap mục tiêu