Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

วิธีบังคับ Garbage Collection ใน C#

GC.Collect() - การบังคับ Garbage Collection ใน C#

Garbage Collector (GC) ใน .NET จัดการ Memory อัตโนมัติ แต่มีเมธอด GC.Collect() สำหรับบังคับให้ GC ทำงานทันที

⚠️ คำเตือน: ไม่แนะนำให้เรียก GC.Collect() เองใน Production Code
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)
OptimizedRun ถ้ามีประโยชน์ (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 0Object ใหม่สร้างขึ้น (Short-lived)บ่อยมาก (Fast, Cheap)
Gen 1Object ที่รอดจาก Gen 0 (Buffer)ปานกลาง
Gen 2Object ที่มีอายุยาว (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 GCSingle-threaded GC, 低 Latency, 適合 Client AppDefault (Non-Server)
Server GCMulti-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