Garbage Collector (GC) ใน .NET เป็น **Automatic Memory Manager** ที่จัดการการจัดสรร (Allocation) และการปล่อย (Release) หน่วยความจำอัตโนมัติ ทำให้นักพัฒนาไม่ต้องจัดการ Memory ด้วยตนเอง (Manual Memory Management)
หลักการทำงานพื้นฐาน
- Allocation: ทุกครั้งที่
newObject หรือ Boxing Value Type → จัดสรรบน Managed Heap - Root Tracking: GC ตรวจสอบ Object Graph จาก Roots (Stack, Static Fields, CPU Registers)
- Mark Phase: ทำเครื่องหมาย Object ที่เข้าถึงได้ (Reachable)
- Sweep/Compact Phase: ลบ Object ที่ไม่เข้าถึงได้ (Unreachable) และ Compact Heap
Generational GC (รุ่นของ GC)
.NET ใช้ **Generational Garbage Collection** แบ่ง Heap เป็น 3 Generations:
| Generation | ลักษณะ | ความถี่ GC |
|---|---|---|
| Gen 0 | Object ใหม่, Short-lived (Local variables) | บ่อยที่สุด (Fast, Cheap) |
| Gen 1 | Object ที่รอดจาก Gen 0, Buffer/Intermediate | ปานกลาง |
| Gen 2 | Long-lived Objects (Static fields, Singletons, Cache) | น้อยที่สุด (Expensive, Full GC) |
| LOH (Large Object Heap) | Object ≥ 85,000 bytes (Arrays, Large Strings) | Collected with Gen 2, No Compaction |
ข้อดีของ Garbage Collection
- ✅ **ไม่ต้อง Free Memory เอง** - ลด Bugs: Memory Leaks, Double Free, Use-after-free
- ✅ **Allocation เร็วมาก** - Pointer Bump Allocation (แค่เลื่อน Pointer)
- ✅ **Memory Safety** - ไม่มี Dangling Pointers, Buffer Overflows
- ✅ **Compaction** - ลด Fragmentation, Improve Cache Locality
- ✅ **Clean Objects** - Object ใหม่ได้ Zero-initialized อัตโนมัติ
GC Modes (.NET Core / .NET 5+)
| Mode | คำอธิบาย | เหมาะสำหรับ |
|---|---|---|
| Workstation GC | Concurrent GC, Low Latency, Single Threaded GC | Client Apps (WPF, WinForms, MAUI) |
| Server GC | Multiple Heaps (1 per Logical CPU), Parallel GC, Higher Throughput | Server Apps (ASP.NET Core, Services) |
true
true
IDisposable & Finalizers - จัดการ Unmanaged Resources
GC จัดการ **Managed Memory** เท่านั้น สำหรับ Unmanaged Resources (File Handles, DB Connections, Native Memory) ต้องใช้ Pattern นี้:
public class ResourceHolder : IDisposable {
private IntPtr _unmanagedHandle;
private bool _disposed = false;
// Finalizer (Destructor) - Safety Net
~ResourceHolder() {
Dispose(false);
}
// Public Dispose
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this); // บอก GC ว่า Finalize ไม่ต้องรัน
}
// Core Cleanup Logic
protected virtual void Dispose(bool disposing) {
if (_disposed) return;
if (disposing) {
// Free Managed Resources (Managed Objects)
}
// Free Unmanaged Resources (เสมอ)
if (_unmanagedHandle != IntPtr.Zero) {
NativeMethods.CloseHandle(_unmanagedHandle);
_unmanagedHandle = IntPtr.Zero;
}
_disposed = true;
}
}
// ใช้งานกับ using (Auto Dispose)
using (var resource = new ResourceHolder()) {
// ใช้งาน
} // Dispose() เรียกอัตโนมัติ
GC API ที่น่าสนใจ
// บังคับให้ GC รัน (ไม่แนะนำให้ใช้บ่อย)
GC.Collect();
GC.Collect(2, GCCollectionMode.Forced, blocking: true);
// ตรวจสอบ Generation
int gen = GC.GetGeneration(obj);
// Memory Pressure - บอก GC ว่ามี Unmanaged Memory มาก
GC.AddMemoryPressure(bytesAllocated);
GC.RemoveMemoryPressure(bytesReleased);
// Weak Reference - อ้างอิงที่ไม่ป้องกัน GC
WeakReference weak = new WeakReference(obj);
if (weak.TryGetTarget(out var target)) {
// Object ยังมีชีวิต
}
Best Practices
- ใช้
using/using varสำหรับIDisposableทุกครั้ง - หลีกเลี่ยง Finalizer (~ClassName) ถ้าไม่จำเป็น - เพิ่ม Overhead ให้ GC
- ไม่ควรเรียก
GC.Collect()เอง ยกเว้นกรณีพิเศษ (Testing, Memory Profiling) - ใช้
Span<T>,Memory<T>,ArrayPool<T>ลด Allocation - ตั้งค่า
ServerGarbageCollectionสำหรับ Server Workloads - Monitor GC ด้วย
dotnet-counters,dotnet-gcdump, PerfView
Memory Pressure Demo
// แสดง Gen 0/1/2 Collections
Console.WriteLine($"Gen 0: {GC.CollectionCount(0)}");
Console.WriteLine($"Gen 1: {GC.CollectionCount(1)}");
Console.WriteLine($"Gen 2: {GC.CollectionCount(2)}");
Console.WriteLine($"Allocated: {GC.GetTotalMemory(false):N0} bytes");