Garbage Collector (GC) ใน .NET เป็น Automatic Memory Manager จัดการหน่วยความจำ Managed Heap ให้อัตโนมัติ
หลักการทำงานพื้นฐาน
- Allocation: `new` Object → 配置บน Managed Heap (Pointer Bump - เร็วมาก)
- Root Tracking: GC รู้จัก "Roots" (Stack Variables, Static Fields, CPU Registers)
- Mark Phase: Traverse จาก Roots ทุก Object ที่ Reachable = Live
- Sweep/Compact Phase: Unreachable = Garbage → Reclaim Memory, Compact Heap
- Generations: Gen 0 (New), Gen 1 (Survived 1 GC), Gen 2 (Long-lived)
Generational GC
| Generation | อายุ Object | ความถี่ GC | ขนาด Heap |
|---|---|---|---|
| Gen 0 | Short-lived (New Objects) | บ่อยมาก | เล็ก (~256KB-4MB) |
| Gen 1 | Survived Gen 0 | ปานกลาง | กลาง |
| Gen 2 | Long-lived (Static, Large Objects) | น้อย | ใหญ่ |
| LOH | >= 85,000 bytes | เฉพาะ Gen 2 GC | แยกต่างหาก (ไม่ Compact) |
ประโยชน์ของ GC
- ไม่ต้อง Free Memory ด้วยตนเอง (No Manual Memory Management)
- ป้องกัน Memory Leaks (ส่วนใหญ่), Dangling Pointers, Double Free
- Allocation เร็วมาก (Pointer Bump - เพียง Increment Pointer)
- Memory Safety: Type Safety + Bounds Checking
- Objects ใหม่ได้ Zero-initialized อัตโนมัติ
GC Modes
// Workstation GC (Default for Client Apps) - Low Latency, Concurrent
// Server GC (Default for ASP.NET Core, Services) - High Throughput, Multi-Heap
// Configure in .csproj or runtimeconfig.json
true
true
GC APIs ที่ควรรู้
// Force GC (ไม่แนะนำให้ใช้บ่อย - ให้ GC จัดการเอง)
GC.Collect(); // Full GC (All Generations)
GC.Collect(2); // Gen 2 Only
GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
// Query Info
int gen = GC.GetGeneration(obj); // Object Generation
long bytes = GC.GetTotalMemory(false); // Allocated Bytes (approx)
bool isServer = GCSettings.IsServerGC; // GC Mode
// Large Object Heap (LOH) Compaction (.NET 4.5.1+)
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
IDisposable & Finalizer Pattern
public class ResourceHolder : IDisposable
{
private IntPtr _unmanagedResource;
private bool _disposed;
// Finalizer (Destructor Syntax) - Safety Net
~ResourceHolder() => Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); // ข้าม Finalizer เพราะ Cleaned แล้ว
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// Free Managed Resources
}
// Free Unmanaged Resources
if (_unmanagedResource != IntPtr.Zero)
{
Marshal.FreeHGlobal(_unmanagedResource);
_unmanagedResource = IntPtr.Zero;
}
_disposed = true;
}
}
Best Practices
- อย่า Force GC ยกเว้น Testing/Profiling/สถานการณ์พิเศษ
- Implement IDisposable สำหรับ Unmanaged Resources (File, DB, Network, Native)
- ใช้ `using` / `using var` ให้ Dispose ถูกต้อง
- หลีกเลี่ยง Finalizer ถ้าไม่จำเป็น (Overhead, Non-deterministic)
- หลีกเลี่ยง Large Objects ใน Hot Path (LOH Fragmentation)
- Object Pooling สำหรับ Objects ที่สร้าง/ทิ้งบ่อย (ArrayPool, MemoryPool)
Monitoring GC
// ETW Events, PerfView, dotnet-counters, dotnet-gcdump
dotnet-counters monitor --process-id System.Runtime
// Gen 0/1/2 GC Count, Heap Size, Allocation Rate, Pause Time