GC.Collect() - การบังคับ Garbage Collection ใน C#
Garbage Collector (GC) ใน .NET จัดการ Memory อัตโนมัติ แต่มีเมธอด GC.Collect() สำหรับบังคับให้ GC ทำงานทันที
⚠️ คำเตือน: ไม่แนะนำให้เรียก GC.Collect() เองใน Production Code
GC ของ .NET มี Generational Algorithm ที่ฉลาดพอจะจัดการได้เอง การบังคับอาจทำให้:
GC ของ .NET มี Generational Algorithm ที่ฉลาดพอจะจัดการได้เอง การบังคับอาจทำให้:
- Performance Overhead (Stop-the-world, Full GC)
- Promote Object ไป Generation สูงกว่าที่ควร (ลดประสิทธิภาพ GC ต่อไป)
- Fragmentation เข้า Gen 2 ที่ยากต่อการ Compact
เมื่อไหร่纔ควรใช้ GC.Collect()?
- Testing / Benchmarking (เพื่อสร้าง Baseline)
- หลังปล่อย Resource ใหญ่ๆ (Large Array, Unmanaged Resource)
- App Domain Unload / Plugin Unload
- Low-memory Environment (Embedded, Container limit)
- Diagnostic / Profiling
Syntax และ Overloads
// บังคับ GC ทุก Generation (Full GC) - 重い
GC.Collect();
// บังคับ GC จาก Gen 0 ถึง Generation ที่ระบุ
GC.Collect(int generation); // 0, 1, หรือ 2
GC.Collect(int generation, GCCollectionMode mode);
GC.Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting);
GCCollectionMode
| Mode | พฤติกรรม |
|---|---|
Default | ให้ GC ตัดสินใจ (อาจไม่ Run ทันที) |
Forced | บังคับ Run ทันที (Blocking) |
Optimized | Run ถ้ามีประโยชน์ (Productivity mode) |
ตัวอย่างโค้ด
using System;
class Program
{
static void Main()
{
Console.WriteLine("=== GC.Collect Demo ===");
// 1. สร้าง Object เยอะ (Gen 0)
MakeSomeGarbage();
// 2. ตรวจสอบ Memory ก่อน GC
long before = GC.GetTotalMemory(false);
Console.WriteLine($"Memory before GC: {before:N0} bytes");
Console.WriteLine($"Gen 0 Collections: {GC.CollectionCount(0)}");
Console.WriteLine($"Gen 1 Collections: {GC.CollectionCount(1)}");
Console.WriteLine($"Gen 2 Collections: {GC.CollectionCount(2)}");
// 3. บังคับ Full GC
GC.Collect();
GC.WaitForPendingFinalizers(); // รอ Finalizer
GC.Collect(); // รอบที่ 2 เพื่อ Cleanup Finalized Objects
// 4. ตรวจสอบ Memory หลัง GC
long after = GC.GetTotalMemory(true); // true = force wait
Console.WriteLine($"\nMemory after GC: {after:N0} bytes");
Console.WriteLine($"Freed: {(before - after):N0} bytes");
Console.WriteLine($"Gen 0 Collections: {GC.CollectionCount(0)}");
Console.WriteLine($"Gen 1 Collections: {GC.CollectionCount(1)}");
Console.WriteLine($"Gen 2 Collections: {GC.CollectionCount(2)}");
}
static void MakeSomeGarbage()
{
const int count = 100_000;
for (int i = 0; i < count; i++)
{
// สร้าง Object แล้วทิ้ง (Eligible for GC)
var obj = new byte[1024]; // 1KB each
// obj ออก Scope ที่นี่
}
}
}
Generations ใน .NET GC
| Generation | คำอธิบาย | ความถี่ GC |
|---|---|---|
| Gen 0 | Object ใหม่สร้างขึ้น (Short-lived) | บ่อยมาก (Fast, Cheap) |
| Gen 1 | Object ที่รอดจาก Gen 0 (Buffer) | ปานกลาง |
| Gen 2 | Object ที่มีอายุยาว (Long-lived) | น้อย (Slow, Expensive) |
| LOH (Large Object Heap) | Object > 85,000 bytes | รวมกับ Gen 2 (ไม่ Compact โดย Default) |
GC.GetTotalMemory()
// forceFullCollection = false: คืนค่าประมาณ (Fast)
long approx = GC.GetTotalMemory(false);
// forceFullCollection = true: บังคับ GC แล้วคืนค่า (Slow, Accurate)
long exact = GC.GetTotalMemory(true);
ตัวอย่าง: WeakReference (สังเกต GC)
using System;
class Program
{
static void Main()
{
var obj = new StringBuilder("Hello");
var weakRef = new WeakReference(obj);
Console.WriteLine($"Before GC: IsAlive = {weakRef.IsAlive}"); // True
obj = null; // ล้าง Strong Reference
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine($"After GC: IsAlive = {weakRef.IsAlive}"); // False (ถ้า GC รัน)
}
}
GC ใน .NET Core / .NET 5+ (Server vs Workstation GC)
| โหมด | คำอธิบาย | Config |
|---|---|---|
| Workstation GC | Single-threaded GC, 低 Latency, 適合 Client App | Default (Non-Server) |
| Server GC | Multi-threaded GC (1 Heap/CPU), Throughput 优先, 適合 Web Server | <ServerGarbageCollection>true</ServerGarbageCollection> ใน .csproj |
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!-- Concurrent GC (Background GC) - Default On -->
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
</PropertyGroup>
</Project>
สรุป
GC.Collect()บังคับ Full GC (Gen 0, 1, 2)GC.Collect(gen)บังคับ GC จนถึง Generation ที่ระบุ- อย่าเรียกใน Production ปกติ - ให้ GC ทำงานอัตโนมัติ
- ใช้
GC.GetTotalMemory(true)วัด Memory Accurate - Server GC สำหรับ Web App, Workstation GC สำหรับ Desktop
- Finalizer: ใช้
GC.WaitForPendingFinalizers()หลัง Collect