2008-08-21 42 views
20

Tất cả những cách có thể có mà chúng ta có thể bị rò rỉ bộ nhớ trong .NET là gì?Rò rỉ bộ nhớ trong .NET

tôi biết hai:

  1. Không đúng un-đăng ký Event Handlers/Delegates.
  2. Không xử lý điều khiển con năng động trong Windows Forms:

Ví dụ:

// Causes Leaks 
Label label = new Label(); 
this.Controls.Add(label); 
this.Controls.Remove(label); 

// Correct Code 
Label label = new Label(); 
this.Controls.Add(label); 
this.Controls.Remove(label); 
label.Dispose(); 

Cập nhật: Ý tưởng là để liệt kê cạm bẫy phổ biến mà không phải là quá rõ ràng (như trên). Thông thường khái niệm là rò rỉ bộ nhớ không phải là một vấn đề lớn vì bộ thu gom rác. Không giống như nó được sử dụng trong C + +.


Những người thảo luận tuyệt vời, nhưng hãy để tôi làm rõ ... theo định nghĩa, nếu không có tham chiếu đến đối tượng trong .NET, nó sẽ được Thu thập rác tại một thời điểm. Vì vậy, đó không phải là một cách để gây ra rò rỉ bộ nhớ.

Trong môi trường được quản lý, tôi sẽ coi đó là rò rỉ bộ nhớ nếu bạn có tham chiếu không mong muốn đối với bất kỳ đối tượng nào mà bạn không biết (do đó có hai ví dụ trong câu hỏi của tôi).

Vì vậy, các cách có thể khác nhau trong đó rò rỉ bộ nhớ có thể xảy ra là gì?

+2

Như Keith đã nói, mẫu của bạn không gây rò rỉ bộ nhớ. – tobsen

Trả lời

5

Chặn chuỗi trình hoàn thiện. Không có đối tượng nào khác sẽ được thu gom rác cho đến khi luồng kết thúc được bỏ chặn. Do đó, lượng bộ nhớ được sử dụng sẽ tăng lên và phát triển.

Đọc thêm: http://dotnetdebug.net/2005/06/22/blocked-finalizer-thread/

+11

Điều đó có nghĩa là gì? –

+0

Trình hoàn thiện là một luồng. 'Hoàn thiện' là những gì xảy ra khi một vật thể có thể xử lý được, cuối cùng, được xử lý. Nếu một mục cụ thể không thể được xử lý, thì không có gì được xử lý, và bạn sẽ hết bộ nhớ. –

+0

vậy điều gì sau đó có nghĩa là 'chặn' trong ngữ cảnh - ghi đè và mã-bọc chức năng finalizer, hoặc hoàn toàn ngăn chặn nó chạy? – Hardryv

14

Không có cách nào để cung cấp danh sách toàn diện ... điều này giống như hỏi "Làm thế nào bạn có thể bị ướt?"

Điều đó nói rằng, đảm bảo bạn đang gọi Dispose() trên mọi thứ triển khai IDisposable và đảm bảo bạn triển khai IDisposable trên mọi loại tiêu thụ tài nguyên không được quản lý dưới bất kỳ hình thức nào.

Mọi lúc, hãy chạy một cái gì đó như FxCop trên codebase của bạn để giúp bạn thực thi quy tắc đó - bạn sẽ ngạc nhiên về việc một số đối tượng dùng một lần được chôn sâu trong một khung ứng dụng.

+0

Bạn sẽ thiết lập FxCop để thực thi quy tắc đó như thế nào? – Joel

2

Bạn đang nói về việc sử dụng bộ nhớ không mong muốn hoặc rò rỉ thực sự? Hai trường hợp bạn liệt kê không chính xác bị rò rỉ; chúng là những trường hợp đối tượng dính xung quanh lâu hơn dự định.

Nói cách khác, chúng là tài liệu tham khảo người gọi chúng là rò rỉ bộ nhớ không biết hoặc quên.

Chỉnh sửa: Hoặc chúng là các lỗi thực sự trong bộ thu gom rác hoặc mã không được quản lý.

Chỉnh sửa 2: Một cách khác để suy nghĩ về điều này là luôn đảm bảo các tham chiếu bên ngoài cho các đối tượng của bạn được phát hành một cách thích hợp. Bên ngoài có nghĩa là mã ngoài tầm kiểm soát của bạn. Bất kỳ trường hợp nào xảy ra là trường hợp bạn có thể "rò rỉ" bộ nhớ.

0

Rất nhiều thứ có thể gây rò rỉ bộ nhớ trong ngôn ngữ không được quản lý vẫn có thể gây rò rỉ bộ nhớ trong ngôn ngữ được quản lý. Ví dụ: bad caching policies có thể dẫn đến rò rỉ bộ nhớ.

Nhưng như Greg và Danny đã nói, không có danh sách toàn diện. Bất cứ điều gì có thể dẫn đến việc giữ bộ nhớ sau khi tuổi thọ hữu ích của nó có thể gây ra rò rỉ.

2

Gọi IDisposable mỗi lần là nơi dễ nhất để bắt đầu, và chắc chắn là một cách hiệu quả để lấy tất cả các trái cây bị rò rỉ bộ nhớ treo thấp trong codebase. Tuy nhiên, nó không phải lúc nào cũng đủ. Ví dụ, nó cũng quan trọng để hiểu làm thế nào và khi mã được quản lý được tạo ra khi chạy, và khi các assembly được nạp vào miền ứng dụng, chúng sẽ không bao giờ được tải xuống, có thể tăng dấu chân ứng dụng.

+2

Không đời nào. Phát triển trên Compact Framework trong. Net và bạn sẽ nhanh chóng tìm ra rằng thiết bị của bạn gõ ra khỏi bộ nhớ trong một vội vàng nếu bạn không vứt bỏ đúng đối tượng của bạn, do đó bạn không nên thực sự khái quát ở đây. Bạn không thể đợi cho GC làm điều đó. –

21

Điều đó không thực sự gây ra rò rỉ, nó chỉ làm cho công việc nhiều hơn cho các GC:

// slows GC 
Label label = new Label(); 
this.Controls.Add(label); 
this.Controls.Remove(label); 

// better 
Label label = new Label(); 
this.Controls.Add(label); 
this.Controls.Remove(label); 
label.Dispose(); 

// best 
using(Label label = new Label()) 
{ 
    this.Controls.Add(label); 
    this.Controls.Remove(label); 
} 

Rời thành phần dùng một lần nằm xung quanh như thế này không bao giờ là nhiều của một vấn đề trong một môi trường được quản lý như Net - đó là một phần lớn của phương tiện được quản lý.

Bạn sẽ làm chậm ứng dụng của bạn xuống, chắc chắn. Nhưng bạn sẽ không để lại một mớ hỗn độn cho bất cứ điều gì khác.

+1

Rất nguy hiểm khi rời khỏi Điều khiển nằm xung quanh. Đó là ok cho các ứng dụng nhỏ throwaway, nhưng bạn nên luôn luôn vứt bỏ chúng trong các ứng dụng thực tế hoặc bạn sẽ hối tiếc. Tin tôi đi, tôi đã ở đó. – Niki

+1

Tôi đã đến đây, đó là một điều nguy hiểm - để lại các vật có thể tháo rời xung quanh! Để kiểm tra điều này, hãy tạo và thả System.Drawing.Bitmap trong một vòng lặp - bạn sẽ sớm nhận được OutOfMemoryException và GC sẽ không trợ giúp. –

+1

@modosansreves - vâng chắc chắn sẽ phá vỡ ứng dụng của bạn. Tuy nhiên, vì bạn nhận được một 'OutOfMemoryException' được quản lý, bạn chỉ gặp phải ứng dụng đó.Một rò rỉ bộ nhớ không được quản lý sẽ BSOD, treo hoặc làm hỏng toàn bộ máy. – Keith

4

Ngoại lệ trong các phương thức hoàn thiện (hoặc loại bỏ cuộc gọi từ trình kết thúc) để ngăn không cho các tài nguyên không được quản lý được xử lý đúng cách. Một thông thường là do lập trình viên giả định đối tượng đặt hàng nào sẽ được xử lý và cố gắng giải phóng đối tượng ngang hàng đã được xử lý dẫn đến ngoại lệ và phần còn lại của phương thức Finalize/Dispose from Finalize không được gọi.

0

Chủ đề bị khóa sẽ không bao giờ giải phóng gốc. Rõ ràng bạn có thể lập luận rằng bế tắc trình bày một vấn đề lớn hơn.

Chuỗi kết thúc bế tắc sẽ ngăn tất cả các trình hoàn thành còn lại chạy và do đó ngăn tất cả các đối tượng cuối cùng bị khai hoang (vì chúng vẫn đang được bắt nguồn bởi danh sách có thể truy cập).

Trên máy đa CPU, bạn có thể tạo đối tượng cuối cùng nhanh hơn chuỗi kết thúc có thể chạy finalizers. Miễn là điều đó được duy trì, bạn sẽ "rò rỉ" bộ nhớ. Nó có lẽ không phải là rất có khả năng rằng điều này sẽ xảy ra trong tự nhiên, nhưng nó rất dễ dàng để tái sản xuất.

Heap đối tượng lớn không được nén chặt, vì vậy bạn có thể rò rỉ bộ nhớ thông qua phân mảnh.

Có một số đối tượng phải được giải phóng thủ công. Ví dụ. remoting đối tượng không có thuê và lắp ráp (phải dỡ bỏ AppDomain).

14

Đặt số GridControl.DataSource trực tiếp mà không cần sử dụng phiên bản của lớp BindingSource (http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx).

này gây ra rò rỉ trong ứng dụng của tôi đó đã cho tôi nhiều thời gian để theo dõi với một hồ sơ, cuối cùng tôi đã tìm thấy báo cáo lỗi này mà Microsoft trả lời: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

Thật buồn cười rằng trong tài liệu hướng dẫn cho lớp BindingSource Microsoft cố gắng để vượt qua nó như là một người bạn hợp pháp cũng nghĩ ra lớp, nhưng tôi nghĩ rằng họ chỉ tạo ra nó để giải quyết một rò rỉ cơ bản liên quan đến quản lý tiền tệ và dữ liệu ràng buộc để điều khiển lưới.

Xem ra cho điều này, tôi đặt cược có vô số các ứng dụng bị rò rỉ ngoài đó vì điều này!

+7

Bài đăng độc ác. Chúng tôi đã có một ứng dụng di động mà chúng tôi đã siêng năng dọn dẹp tài nguyên và đảm bảo mọi thứ triển khai IDisposable được thực hiện. Ngay cả sau khi tất cả điều đó, chúng tôi vẫn còn có những vụ tai nạn kỳ lạ trong lĩnh vực này sau khi sử dụng nặng .. Chúng tôi đã có kịch bản chính xác này! Bạn đá. –

+1

lol +1 cho Mat Nadrofsky bình luận – Drevak

1
  1. Tiếp tục tham chiếu đến các đối tượng bạn không còn cần đến nữa.

Trả lời nhận xét khác - một cách để đảm bảo Mục đích được sử dụng khi sử dụng cấu trúc mã cho phép.

2

Để ngăn chặn rò rỉ bộ nhớ NET:

1) Sử dụng các 'sử dụng' xây dựng (hoặc 'thử-cuối cùng xây dựng) bất cứ khi nào một đối tượng với 'giao diện IDisposable' được tạo ra.

2) Tạo các lớp 'IDisposable' nếu chúng tạo chuỗi hoặc thêm đối tượng vào bộ sưu tập tĩnh hoặc dài. Nhớ một sự kiện C# 'là một bộ sưu tập.

Đây là một bài viết ngắn về Tips to Prevent Memory Leaks.

1

Một điều đó là thực sự bất ngờ đối với tôi là thế này:

Region oldClip = graphics.Clip; 
using (Region newClip = new Region(...)) 
{ 
    graphics.Clip = newClip; 
    // draw something 
    graphics.Clip = oldClip; 
} 

ở đâu rò rỉ bộ nhớ? Phải, bạn cũng nên xử lý oldClip! Bởi vì Graphics.Clip là một trong những thuộc tính hiếm hoi trả về một đối tượng dùng một lần mới mỗi khi trình thu thập được gọi.

3

Tôi có 4 mục bổ sung để thêm vào cuộc thảo luận này:

  1. đề Chấm dứt (Thread.Abort()) đã tạo ra giao diện người dùng điều khiển mà không đúng cách chuẩn bị cho một sự kiện như vậy có thể dẫn đến bộ nhớ đang được sử dụng chờ đợi .

  2. Truy cập tài nguyên không được quản lý thông qua Pinvoke và không làm sạch chúng có thể dẫn đến rò rỉ bộ nhớ.

  3. Sửa đổi các đối tượng chuỗi lớn. Không nhất thiết là một rò rỉ bộ nhớ, một khi nằm ngoài phạm vi, GC sẽ chăm sóc nó, tuy nhiên, hiệu suất khôn ngoan, hệ thống của bạn có thể bị ảnh hưởng nếu chuỗi lớn được sửa đổi thường xuyên bởi vì bạn không thực sự phụ thuộc vào GC để đảm bảo in chân của chương trình tối thiểu.

  4. Tạo đối tượng GDI thường xuyên để thực hiện vẽ tùy chỉnh. Nếu thực hiện GDI làm việc thường xuyên, hãy sử dụng lại một đối tượng gdi duy nhất.

1

Tess Fernandez Có các bài đăng trên blog tuyệt vời về tìm và gỡ lỗi rò rỉ bộ nhớ. Lab 6Lab 7