2010-04-19 32 views
6

Tôi đã xem xét một số hành vi tò mò liên quan đến thu gom rác trong .Net.Tiêu chí để kích hoạt thu gom rác trong .Net

Chương trình sau sẽ ném OutOfMemoryException rất nhanh (sau ít hơn một giây trên máy 32 bit, 2GB). Trình hoàn thiện Foo không bao giờ được gọi.

class Foo 
{ 
    Guid guid = Guid.NewGuid(); 
    byte[] buffer = new byte[1000000]; 

    static Random rand = new Random(); 
    public Foo() 
    { 
     // Uncomment the following line and the program will run forever. 
     // rand.NextBytes(buffer); 
    } 

    ~Foo() 
    { 
     // This finalizer is never called unless the rand.NextBytes 
     // line in the constructor is uncommented. 
    } 

    static public void Main(string args[]) 
    { 
     for (; ;) 
     { 
      new Foo(); 
     } 
    } 
} 

Nếu dòng rand.nextBytes không được chú ý, nó sẽ chạy quảng cáo vô hạn và trình kết thúc Foo thường được gọi. Tại sao vậy?

Đoán tốt nhất của tôi là trong trường hợp trước đó, CLR hoặc Windows VMM là lười về phân bổ bộ nhớ vật lý. Bộ đệm không bao giờ được ghi vào, do đó bộ nhớ vật lý không bao giờ được sử dụng. Khi không gian địa chỉ hết, hệ thống bị treo. Trong trường hợp thứ hai, hệ thống hết bộ nhớ vật lý trước khi nó hết dung lượng địa chỉ, GC được kích hoạt và các đối tượng được thu thập.

Tuy nhiên, đây là phần tôi không nhận được. Giả sử lý thuyết của tôi là chính xác, tại sao GC không kích hoạt khi không gian địa chỉ chạy thấp? Nếu lý thuyết của tôi là không chính xác, thì giải thích thực sự là gì?

Trả lời

1

Mã chạy ở mức 18MB ổn định trên máy của tôi, có hoặc không có dòng đó (XP SP3 x86, .Net 3.5 SP1, lõi kép).

Có thể những gì đang xảy ra trên máy của bạn là khi dòng được nhận xét, chương trình dành phần lớn thời gian phân bổ và quản lý phân bổ quá nhiều bộ nhớ trước khi chuỗi thu rác có cơ hội giải quyết nó. Khi bạn bỏ ghi chú dòng đó, chương trình dành ít thời gian phân bổ nhiều hơn, và do đó không thể phân bổ quá nhiều trước khi luồng GC chạy.

Thử thay thế dòng đã nhận xét bằng Thread.Sleep(0); nếu nó không sụp đổ, tôi có lẽ đúng.


Cũng giống như một lưu ý phụ, bạn không bao giờ nên dựa vào finalizer - nó không đảm bảo được gọi ngay lập tức khi đối tượng được GC'ed, hoặc thậm chí cả. Thay vào đó, trong mã thực thực hiện giao diện IDisposable và chỉ sử dụng trình hoàn tất nếu cực kỳ quan trọng, ngay cả khi lập trình viên quên nó (ví dụ: phát hành tài nguyên mạng/tệp được chia sẻ, v.v.)

+0

I 'không chắc chắn bạn có câu trả lời đúng, nhưng bạn đưa tôi vào một ca khúc thú vị. Tôi tắt hai máy tính ảo mà tôi đang chạy (cả với 512MB) và tôi nhận được hành vi của bạn. Khi tôi nạp các VPCs trở lại, tôi đã nhận được hành vi lỗi ban đầu. –

+0

Ngoài ra còn có một số chức năng thú vị trong lớp 'GC' để kiểm tra, nhưng một lần nữa, trong mã thực sự không bao giờ được sử dụng. –

+0

Với VPC đang chạy và Thread.Sleep (0) tại chỗ, chương trình sẽ chạy mãi mãi. Bây giờ câu hỏi thay đổi ... Tại sao GC không tự động được kích hoạt khi lỗi phân bổ? –