2014-09-05 25 views
6

Tôi đang cố gắng theo dõi rò rỉ bộ nhớ trong một chương trình C# lớn hơn để sinh ra nhiều luồng. Trong quá trình này, tôi đã tạo ra một chương trình nhỏ mà tôi đang sử dụng để kiểm tra một số điều cơ bản và tôi đã tìm thấy một số hành vi mà tôi thực sự không hiểu.Rò rỉ bộ nhớ chủ đề

class Program 
{ 
    static void test() 
    { 
    } 

    static void Main(string[] args) 
    { 
     while (true) 
     {    
      Thread test_thread = new Thread(() => test()); 
      test_thread.Start(); 
      Thread.Sleep(20); 
     } 
    } 
} 

Chạy chương trình này, tôi thấy rằng việc sử dụng bộ nhớ của chương trình tăng đều mà không dừng. Chỉ trong vài phút, mức sử dụng bộ nhớ đạt hơn 100MB và tiếp tục leo. Nếu tôi nhận xét ra dòng test_thread.Start() ;, bộ nhớ được chương trình sử dụng tối đa khoảng một vài megabyte và mức độ ra ngoài. Tôi cũng đã cố gắng buộc thu gom rác ở cuối vòng lặp while bằng cách sử dụng GC.Collect(), nhưng nó dường như không làm gì cả.

Tôi nghĩ rằng chuỗi sẽ bị hủy đăng ký ngay sau khi chức năng kết thúc thực thi cho phép GC quét nó lên, nhưng điều này dường như không xảy ra. Tôi không được hiểu một cái gì đó sâu sắc hơn ở đây, và tôi sẽ đánh giá cao một số trợ giúp với sửa chữa rò rỉ này. Cảm ơn trước!

Trả lời

9

Điều này là do thiết kế, chương trình thử nghiệm của bạn được cho là để trưng bày sử dụng bộ nhớ chạy trốn. Bạn có thể thấy lý do cơ bản từ Taskmgr.exe. Sử dụng View + Select Columns và đánh dấu "Handles". Quan sát cách số lượng xử lý cho quy trình của bạn tăng đều đặn. Sử dụng bộ nhớ đi lên cùng với đó, phản ánh bộ nhớ không được quản lý được sử dụng bởi các đối tượng xử lý.

Lựa chọn thiết kế là một lựa chọn hết sức can đảm, CLR sử dụng 5 đối tượng hệ điều hành trên mỗi chuỗi. Hệ thống ống nước, được sử dụng để đồng bộ hóa. Các đối tượng này là bản thân dùng một lần, lựa chọn thiết kế là không làm cho lớp Thread thực hiện IDisposable. Đó sẽ là một khó khăn trên các lập trình viên .NET, rất khó để thực hiện cuộc gọi Dispose() vào đúng thời điểm. Can đảm không được trưng bày trong thiết kế lớp Task btw, gây ra rất nhiều vắt tay và general advice not to bother.

Đây là không thường là sự cố trong chương trình .NET được thiết kế tốt. Trường hợp GC chạy thường xuyên đủ để làm sạch các đối tượng OS. Và các đối tượng Thread đang tạo ra một cách tiết kiệm, sử dụng ThreadPool cho các luồng chạy rất ngắn giống như chương trình thử nghiệm của bạn sử dụng.

Có thể, chúng tôi không thể thấy chương trình thực sự của bạn. Đừng coi chừng quá nhiều kết luận từ một thử nghiệm tổng hợp như vậy. Bạn có thể xem thống kê GC với Perfmon.exe, cung cấp cho bạn một ý tưởng nếu nó đang chạy thường xuyên đủ. Một profiler bộ nhớ .NET phong nha là vũ khí của sự lựa chọn. GC.Collect() là vũ khí dự phòng. Ví dụ:

static void Main(string[] args) { 
    int cnt = 0; 
    while (true) { 
     Thread test_thread = new Thread(() => test()); 
     test_thread.Start(); 
     if (++cnt % 256 == 0) GC.Collect(); 
     Thread.Sleep(20); 
    } 
} 

Và bạn sẽ thấy nó bị trả lại qua lại, không bao giờ nhận được cao hơn 4 MB.