Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Tham chiếu vòng (Circular references) trong C# và Garbage Collection

Circular reference (Tham chiếu vòng) xảy ra khi hai hoặc nhiều đối tượng tham chiếu lẫn nhau, tạo thành chu trình (A → B → A). Trong ngôn ngữ có quản lý bộ nhớ thủ công (như C++), điều này gây rò rỉ bộ nhớ. Tuy nhiên, trong C#, Garbage Collector (GC) xử lý tự động.

Ví dụ tham chiếu vòng

public class A { public B RefB; }
public class B { public A RefA; }

// Tạo vòng tròn
A a = new A();
B b = new B();
a.RefB = b;
b.RefA = a;
// a và b giờ tham chiếu lẫn nhau

Cách GC xử lý

GC của .NET dùng thuật toán tracing (duyệt đồ thị) từ các GC roots (biến static, biến local trên stack, handle CPU...). Bất kỳ đối tượng nào không thể truy cập được từ GC roots sẽ được coi là rác và thu hồi, dù chúng tham chiếu lẫn nhau.

Quy trình:

  1. Đánh dấu (Mark): Duyệt từ roots, đánh dấu tất cả đối tượng còn "sống" (reachable).
  2. Dọn dẹp (Sweep/Compact): Giải phóng bộ nhớ các đối tượng không được đánh dấu.

⇒ Tham chiếu vòng không gây rò rỉ bộ nhớ trong C# miễn là toàn bộ chu trình không còn bị root giữ.

Khi nào cần can thiệp?

  • Đối tượng nắm tài nguyên unmanaged (file handle, socket, COM object...) → implement IDisposable và dùng using.
  • Event handler: Subscribe event mà không unsubscribe → publisher giữ reference đến subscriber → subscriber không bị GC. Dùng WeakReference hoặc unsubscribe trong Dispose.
  • Cache/static collection giữ object quá lâu → memory leak logic (không phải leak do GC).

Giải pháp thiết kế tránh vòng tham chiếu logic

Dù GC xử lý được, thiết kế tốt nên tránh vòng tham chiếu không cần thiết:

// Thay vì A tham chiếu B, B tham chiếu A
// Dùng Interface để ngắt phụ thuộc chặt chẽ
public interface IB { void DoSomething(); }

public class A { public IB Service; } // A chỉ phụ thuộc abstraction
public class B : IB { public A Owner; } // B implement interface

Tóm lại: C# GC thông minh, tham chiếu vòng không phải vấn đề. Hãy tập trung vào Dispose pattern cho unmanaged resources và unsubscribe event.