首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 視覺化呈現 .NET 垃圾收集器運作流程

     編輯:關於C#

以 WinForms 圖形化模擬 GC 核心概念:物件配置、代系晋升、根可達性判斷、Finalize 队列、Freachable 队列、記憶體不足觸發完整收集。非真實 CLR 實作,僅供教學視覺化。

核心概念速覽

  • 物件配置:newobj → 計算大小 → 檢查 Gen0 空間 → 足夠則配置,否則觸發 GC
  • 根:靜態欄位、區域變數、方法參數、CPU 註冊器,指向 Managed Heap 物件或 null
  • 三代系統:Gen0 (256KB)、Gen1 (2MB)、Gen2 (10MB),GC 自動調整預算
  • Finalize:~Class(),不可控執行時機,GC.Collect()、Gen0 滿、記憶體不足、AppDomain 卸載、CLR 關閉時觸發
  • Dispose:IDisposable,決定釋放,可配合 HandleCollector 誘發 GC

模擬架構:三大類別

// 物件實體
public class DataObject {
    public bool HasFinalizeMethod { get; set; }
    public bool HasRoot { get; set; }
    public int Size { get; set; }          // 模擬記憶體大小 (1~3 單位)
    public Color Color { get; set; }       // 視覺化顏色
    public string Name { get; set; }       // GUID
    public int Generation { get; set; }    // 0/1/2
    public string Tag => HasFinalizeMethod ? "[F]" : "[!F]";
}

// 物件集合
public class DataObjects : IEnumerable<DataObject> {
    List<DataObject> list = new List<DataObject>();
    public void Add(DataObject o) { if (!list.Contains(o)) list.Add(o); }
    public void Remove(DataObject o) { list.Remove(o); }
    public int Count => list.Count;
    public DataObject this[int i] => list[i];
    public IEnumerator<DataObject> GetEnumerator() => list.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

// 管理器:全域集合 + 三代視圖 + Finalize/Freachable 隊列
public class DataObjectManager {
    public DataObjects AllObjects { get; } = new DataObjects();
    public DataObjects FinalizeTable { get; set; } = new DataObjects();
    public Queue<DataObject> FreachableQueue { get; set; } = new Queue<DataObject>();

    public DataObjects GetGeneration(int gen) {
        var g = new DataObjects();
        foreach (var o in AllObjects) if (o.Generation == gen) g.Add(o);
        return g;
    }
    public DataObjects Gen0 => GetGeneration(0);
    public DataObjects Gen1 => GetGeneration(1);
    public DataObjects Gen2 => GetGeneration(2);
}

物件建立流程

void NewObject() {
    var obj = new DataObject {
        HasFinalizeMethod = Rand.Bool(0.3),
        HasRoot = Rand.Bool(0.6),
        Size = Rand.Int(1, 3),
        Color = Rand.Color(),
        Name = Guid.NewGuid().ToString(),
        Generation = 0
    };

    int need = ObjHeaderSize + obj.Size; // 模擬大小
    if (Gen0FreeSpace >= need) {
        AllocateInGen0(obj);
    } else {
        GCCollect(obj); // 空間不足觸發收集
    }
}

void AllocateInGen0(DataObject obj) {
    manager.AllObjects.Add(obj);
    if (obj.HasFinalizeMethod) manager.FinalizeTable.Add(obj);
    RefreshPanels();
}

GC 收集邏輯(分代式)

  1. 清理 Freachable:移除上輪殘留的 Finalize 物件
  2. FinalizeTable → Freachable:無根且有 Finalize 的物件移入隊列
  3. Gen0 掃描:無根無 Finalize → 直接移除;其餘晉升 Gen1
  4. Gen1 滿檢查:空間不足 → 掃描 Gen1,無根無 Finalize 移除,其餘晉升 Gen2
  5. Gen2 滿檢查:空間不足 → 掃描 Gen2 移除無根物件;仍不足 → 全清(模擬 OOM)
void GCCollect(DataObject newObj) {
    // 1. 清理 Freachable
    while (manager.FreachableQueue.Count > 0) {
        manager.AllObjects.Remove(manager.FreachableQueue.Dequeue());
    }
    // 2. FinalizeTable → Freachable
    foreach (var o in manager.FinalizeTable) if (!o.HasRoot) manager.FreachableQueue.Enqueue(o);
    foreach (var o in manager.FreachableQueue) manager.FinalizeTable.Remove(o);

    // 3. Gen0
    var survivors = new DataObjects();
    foreach (var o in manager.Gen0) {
        if (!o.HasRoot && !o.HasFinalizeMethod) manager.AllObjects.Remove(o);
        else { survivors.Add(o); o.Generation++; }
    }
    // 4. 嘗試放入 Gen1
    if (Gen1FreeSpace >= survivors.TotalSize) {
        foreach (var o in survivors) { /* 已晉升 */ }
    } else {
        HandleGen1(survivors); // 遞迴處理 Gen1
    }
    RefreshPanels();
}

視覺化繪製

每代對應一個 Panel,Paint 事件依物件 Size 繪製矩形,顯示 Name、HasRoot、Tag。

void panelGen0_Paint(object sender, PaintEventArgs e) {
    var g = e.Graphics;
    float x = 0;
    foreach (var o in manager.Gen0) {
        g.FillRectangle(new SolidBrush(o.Color), x, 0, o.Size * UnitWidth, 20);
        g.DrawString($"[{o.Name}]{(o.HasRoot?"R":"")}", font, Brushes.Red, x, 0);
        g.DrawString(o.Tag, font, Brushes.Red, x, 22);
        x += o.Size * UnitWidth;
    }
}

隨機根變更模擬物件失去參考

void RandomizeRoots() {
    foreach (var o in manager.AllObjects)
        if (o.HasRoot) o.HasRoot = Rand.Bool(0.9); // 10% 機率失去根
}
主介面:三代面板與控制按鈕
圖 1:GC 視覺化主視窗
Gen0 配置成功
圖 2:Gen0 空間足夠,直接配置
有 Finalize 物件進入 FinalizeTable
圖 3:具 Finalize 物件加入 Finalize 鏈表
Gen0 滿觸發 GC
圖 4:Gen0 空間不足,觸發垃圾收集
GC 過程:清理 Freachable、移動 FinalizeTable
圖 5:GC 步驟視覺化
FinalizeTable 移至 Freachable
圖 6:Finalize 物件移入 Freachable 隊列
Gen0 物件晉升 Gen1
圖 7:存活物件代系晋升
持續配置導致 Gen1 累積
圖 8:Gen1 物件累積
Gen1 滿觸發 Gen2 晋升
圖 9:Gen1 空間不足,觸發 Gen2 處理
Gen2 處理
圖 10:Gen2 清理與晋升
多代晋升後狀態
圖 11:多輪 GC 後三代分佈
根隨機變更效果
圖 12:物件失去根(R 消失),下輪 GC 將被回收

已知問題

  • System.Timers.Timer 觸發 Panel.Refresh() 會跨執行緒存取 UI,需改用 System.Windows.Forms.Timer 或 Invoke
  • 模擬邏輯簡化,非真實 CLR GC 實作

原始碼下載:Jasen.GCShow.rar

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved