2013-06-26 28 views
6

Tôi có một ứng dụng CF mà theo thời gian rò rỉ UserControls. Phải mất một thời gian, nhưng tôi thu hẹp nó xuống, và thậm chí nhân rộng các hành vi trong khuôn khổ đầy đủ (3.5). Vì hành vi tồn tại trong cả hai, tôi không muốn gọi nó là lỗi, nhưng tôi chắc chắn không hiểu tại sao nó lại xảy ra và hy vọng ai đó có thể làm sáng tỏ nó.GC không hoàn thành UserControl?

Vì vậy, tôi tạo một ứng dụng WinForms đơn giản với Biểu mẫu và nút. Nhấp vào nút thay đổi giữa việc tạo UserControl mới và loại bỏ điều khiển đó. Rất đơn giản.

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    UserControl1 m_ctl; 

    private void button1_Click(object sender, EventArgs e) 
    { 
     if (m_ctl == null) 
     { 
      m_ctl = new UserControl1(); 
      m_ctl.Visible = true; 
      this.Controls.Add(m_ctl); 
     } 
     else 
     { 
      this.Controls.Remove(m_ctl); 
      m_ctl.Dispose(); 
      m_ctl = null; 
      GC.Collect(); 
     } 
    } 
} 

Và đây là UserControl. Nó chỉ đơn giản theo dõi số lượng các phiên bản trực tiếp (tức là chưa hoàn thành). Nó không có gì trên nó, nhưng một nhãn duy nhất để tôi có thể xác nhận trực quan nó trên Form.

public partial class UserControl1 : UserControl 
{ 
    private static int m_instanceCount = 0; 

    public UserControl1() 
    { 
     var c = Interlocked.Increment(ref m_instanceCount); 
     Debug.WriteLine("Instances: " + c.ToString()); 

     InitializeComponent(); 
    } 

    ~UserControl1() 
    { 
     var c = Interlocked.Decrement(ref m_instanceCount); 
     Debug.WriteLine("Instances: " + c.ToString()); 
    } 
} 

Điều kỳ lạ ở đây là số lượng cá thể phát triển vô thời hạn. Cuối cùng, trên thiết bị, tôi hết bộ nhớ. Tôi nghi ngờ tôi sẽ trên máy tính là tốt, tôi chỉ không có khuynh hướng nhấp vào nút cho năm tiếp theo.

Bây giờ nếu tôi thay đổi mặc định, phương thức Dispose nhà thiết kế tạo của UserControl như thế này, chỉ cần thêm các cuộc gọi ReRegisterForFinalize:

protected override void Dispose(bool disposing) 
{ 
    if (disposing && (components != null)) 
    { 
     components.Dispose(); 
    } 

    base.Dispose(disposing); 

    if (disposing) 
    { 
     GC.ReRegisterForFinalize(this); 
    } 
} 

Sau đó, nó cư xử chính xác như mong đợi, Hoàn thiện các trường hợp trong bộ sưu tập (khi tay hoặc tự động) .

Vậy tại sao điều này lại xảy ra? Rõ ràng căn cứ đang gọi SuppressFinalize, nhưng chính xác tại sao điều này lại xảy ra, và tại sao trong cái tên Odin là hành vi mặc định?

+0

+1 cho thực sự có một lý do thực sự để xem xét sử dụng GC, – Sayse

+0

Xét Reed (xin lỗi không thể được giúp đỡ về các giải pháp thực tế!) câu trả lời, finalizer trong mã thực sự của bạn làm gì? Không thể gọi trình hoàn thành gây ra sự rò rỉ? – svick

+0

Mã thực sự không có finalizers. Họ có Dispose đã được triển khai, và Bitmaps và những thứ tương tự đang được xử lý. – ctacke

Trả lời

4

Vậy tại sao điều này lại xảy ra? Rõ ràng căn cứ đang gọi SuppressFinalize, nhưng chính xác tại sao điều này lại xảy ra, và tại sao trong cái tên Odin là hành vi mặc định?

Đó là hành vi mặc định cho các lớp học (đúng) triển khai IDisposable. Khi bạn gọi IDisposable.Dispose, hành vi được đề xuất mặc định là chặn hoàn tất, vì lý do chính để hoàn thành là xóa tài nguyên chưa bao giờ được xử lý. Điều này là do hoàn tất là một hoạt động tốn kém - bạn không muốn hoàn thành các đối tượng một cách không cần thiết và nếu được gọi là Dispose, ý nghĩ là bạn đã dọn dẹp các tài nguyên không được quản lý của mình. Mọi bộ nhớ được quản lý sẽ được xử lý bất kể.

Bạn nên ghi đè Dispose và thực hiện giảm dần trong ghi đè Dispose.

Hành vi này được giải thích trong documentation for IDisposable. Mẫu Dispose phương pháp thực hiện cuộc gọi là (từ các tài liệu tham khảo):

public void Dispose() 
{ 
    Dispose(true); 
    // This object will be cleaned up by the Dispose method. 
    // Therefore, you should call GC.SupressFinalize to 
    // take this object off the finalization queue 
    // and prevent finalization code for this object 
    // from executing a second time 
    GC.SuppressFinalize(this); 
} 
+0

Vấn đề duy nhất với điều này là trong môi trường ứng dụng thực tế, bộ nhớ thu thập thông tin khi bạn điều hướng xung quanh ứng dụng và GC không bao giờ khôi phục bộ nhớ đó. May mắn là tất cả các Views của tôi đều xuất phát từ một UserControl cơ sở duy nhất. Khi tôi ReRegisterForFinalize trong cơ sở đó, rò rỉ biến mất. Không có Chế độ xem nào của tôi đã triển khai finalizers. Bằng cách liên kết, điều này có vẻ là nguyên nhân của sự cố. – ctacke

+0

Điều gì khiến tôi từ chối đường dẫn này là sử dụng .NET Memory Profiler, cho biết có những quan điểm đã được "xử lý nhưng chưa được hoàn thành". – ctacke

Các vấn đề liên quan