คู่มือเกมแลค › L10 หน่วยความจำ
GC thrashing (heap เหลือที่ว่างไม่พอ) GC thrashing (heap nearly full)
ID สาเหตุ mem-gc-thrash · ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)
เปิดการ์ดในฉบับหลักที่มีภาพและการทดลอง →
เมื่อข้อมูลที่ยังใช้อยู่ (live data) เข้าใกล้ขีดจำกัดของ heap แม้ GC ทำงานก็แทบไม่มีอะไรให้เก็บคืน GC จึงวนทำงานซ้ำไม่หยุด
ทำไม ผู้เล่นเพิ่มขึ้นช่วงอีเวนต์หรือหน่วยความจำรั่ว ทำให้ข้อมูลที่ยังใช้อยู่เต็มจนใกล้ขีดจำกัดของ heap → ผลคือ GC เก็บคืนได้นิดเดียวจึงต้องทำ Full GC อีกทันที และ GC ใช้ CPU ไปเกือบทั้งหมด → บนหน้าจอ ทั้งเซิร์ฟเวอร์เป็นสโลว์โมชั่นสลับกับค้างซ้ำ ๆ อยู่หลายนาที แล้วปิดตัวเพราะหน่วยความจำไม่พอ
- อาการ
- สโลว์โมชั่น, ค้าง, หลุด
- ปัจจัย
- การหยุดชะงัก
- ใครเจอ
- ทั้งเซิร์ฟเวอร์
- เกิดเมื่อไร
- ช่วงพีคหัวค่ำ, ตอนคนแห่มารวมกัน, ยิ่งเปิดไว้นานยิ่งเป็น
- ผู้รับผิดชอบ
- ผู้รับผิดชอบหลัก พัฒนาเซิร์ฟเวอร์ (ทีมพัฒนาเกม) · ร่วมกับ อินฟราเซิร์ฟเวอร์ (ทีมอินฟรา)
- งานฝั่งทีมพัฒนาเกม
- ตั้ง heap ให้ใหญ่กว่าข้อมูลที่ยังใช้อยู่ตอนพีคอย่างเหลือเฟือ (ปกติ 2 เท่าขึ้นไป), ลดข้อมูลที่ถูกอ้างอิงไว้นานและลดการรั่ว
- งานฝั่งทีมอินฟรา
- ตั้ง alert ตามสัดส่วนเวลาที่ใช้ไปกับ GC, รีสตาร์ตให้เร็วแทนการฝืนรันต่อ, ใช้อินสแตนซ์ที่มีหน่วยความจำเหลือพอให้ขยาย heap ได้
- ตัวเลขที่ควรรู้
- ถ้า GC ใช้เวลาเกิน 10% ของเวลารันทั้งหมด มักถือเป็นสัญญาณอันตราย GC บางตัวของ Java จะแจ้งข้อผิดพลาดหน่วยความจำไม่พอ ถ้าใช้เวลา 98% ไปกับ GC แต่แทบเก็บคืนอะไรไม่ได้
- บนกราฟ
- ชนเพดานแล้วแบนราบ · heap หลัง GC, สัดส่วนเวลาที่ใช้ไปกับ GC
- จุดที่ต้องดู
- heap ที่เหลือหลัง GC ใกล้ heap สูงสุดแค่ไหน และสัดส่วนเวลาที่ใช้ไปกับ GC Java ดู “หลัง GC (ขนาด heap)” ในบรรทัดของ -Xlog:gc และความถี่ของบรรทัด Pause Full, .NET ดู dotnet-counters (.NET 8 ลงไปคือ % Time in GC since last GC, ตั้งแต่ .NET 9 คือค่าที่เพิ่มขึ้นของ dotnet.gc.pause.time), Go ดูช่วงห่างระหว่างบรรทัดของ GODEBUG=gctrace=1
- สัญญาณว่าใช่
- แม้หลัง GC heap ก็ยังเหลือใกล้ค่าสูงสุด Full GC ทำงานติดกันหลายครั้ง และสัดส่วนเวลาที่ใช้ไปกับ GC สูงกว่าปกติมาก (ปกติเกิน 10%) ระหว่างนั้นทิกของทั้งเซิร์ฟเวอร์ช้าลงไปด้วย
- สัญญาณว่าไม่ใช่
- heap หลัง GC ยังมีที่ว่างแต่หยุดนานอย่างเดียว: น่าจะเป็นเรื่องประเภทหรือการตั้งค่า GC (mem-gc)
- วิธีตรวจ
- ใช้เครื่องมือฝั่งอินฟรา (ไม่ต้องใช้โค้ดเกม)
แหล่งอ้างอิง
- The Parallel Collector Oracle
parallel GC จะแจ้ง OutOfMemoryError ถ้าใช้เวลาเกิน 98% ของเวลาทั้งหมดไปกับ GC แล้วเก็บคืน heap ได้น้อยกว่า 2% - Garbage-First Garbage Collector Tuning Oracle
ค่าเริ่มต้นของ G1 (GCTimeRatio=12) จะกำหนดขนาด heap ให้เวลา GC อยู่ที่ประมาณไม่เกิน 8% ของเวลาทั้งหมด, Full GC ที่เกิดจาก heap ถูกใช้สูงเกินไปหาได้จาก Pause Full (G1 Compaction Pause) ใน log - A Guide to the Go Garbage Collector Go
ค่าเริ่มต้น GOGC=100 ทำให้ heap เป้าหมายประมาณ 2 เท่าของ heap ที่ยังใช้อยู่, ถ้าชนขีดจำกัดหน่วยความจำ GC จะทำงานไม่หยุดจนเกิด thrashing, ใช้ GODEBUG=gctrace=1 แสดงผลการติดตาม GC - Garbage Collector Implementation Oracle
บรรทัดของ -Xlog:gc แสดงประเภท GC (Pause Young, Pause Full), “ปริมาณที่ใช้ก่อน GC->ปริมาณที่ใช้หลัง GC (ขนาด heap)” และเวลาที่หยุด - dotnet-counters diagnostic tool .NET
ตั้งแต่ .NET 9 แสดงเป็น dotnet.gc.pause.time ส่วน .NET 8 ลงไปแสดงเป็น % Time in GC since last GC
สาเหตุที่ควรดูประกอบ
ชั้นเดียวกัน: L10 หน่วยความจำ
สาเหตุจากชั้นอื่นที่ทำให้เกิดอาการเดียวกัน (สโลว์โมชั่น)
ดูการ์ดในฉบับหลักที่มีภาพและการทดลอง