2011-01-19 29 views
10

Có rất nhiều câu hỏi SO hỏi làm thế nào để phát hiện các đối tượng IDisposable bị rò rỉ. Có vẻ như câu trả lời là "you can't".Phát hiện "bị rò rỉ" đối tượng IDisposable

Tôi vừa kiểm tra với trường hợp kiểm tra tầm thường nhất, rằng FxCop 10.0 không làm điều đó, ReSharper 4 với MSVS2010 không làm điều đó.

Điều này có vẻ sai với tôi, tệ hơn rò rỉ bộ nhớ trong C (ít nhất chúng tôi đã thiết lập các công cụ để phát hiện). Tôi đã suy nghĩ: Liệu có thể, bằng cách sử dụng sự phản chiếu và các kỹ thuật tiên tiến khác mơ hồ, mà tôi có thể tiêm một kiểm tra vào thời gian chạy, trong finalizer để xem liệu Dispose đã được gọi là bao nhiêu chưa? Có phải không?

Làm thế nào về các thủ thuật ảo với WinDBG + SOS?

Thậm chí nếu không có công cụ hiện có để thực hiện điều đó, tôi muốn biết liệu điều này có thể về mặt lý thuyết (C# của tôi không phải là rất sắc nét).

Ý tưởng?

LƯU Ý Tiêu đề của câu hỏi này có thể gây hiểu nhầm. Câu hỏi thực sự ở đây phải là đối tượng IDisposable có là Disposed() đúng cách không. Việc xử lý GC không được tính vì tôi coi đó là một sai lầm.

Chỉnh sửa: Giải pháp: .NET Memory Profiler thực hiện công việc. Chúng tôi chỉ cần spam một số GC.Collect() ở cuối chương trình để cho phép trình thu thập thông tin của chúng tôi thu thập chính xác số liệu thống kê.

+0

Lý do tại sao các công cụ tồn tại cho C++ nhưng có thể không dành cho C# là tài nguyên trong C# về cơ bản khác nhau vì tài nguyên không được quản lý * không còn kết hợp với đối tượng *. Những gì có thể được truy tìm, cả trong C# và trong C++, là đối tượng suốt đời và cho dù một đối tượng đã được xử lý đúng cách. Nhưng các nguồn tài nguyên dùng một lần trong C# không có bất kỳ cách nào ràng buộc với đối tượng suốt đời, khiến cho việc theo dõi chúng trở nên khó khăn hơn nhiều. Để so sánh, hãy thử theo dõi các tài nguyên GDI bị rò rỉ không bị ràng buộc qua RAII để đối tượng tồn tại trong C++. Không dễ dàng như vậy. –

+0

Tôi đã cân nhắc điều này một chút. Tôi đã phát triển một thói quen kiểm tra nhanh các kiểu khi tôi viết mã để xem chúng có kế thừa từ 'IDisposable' hay không. Nếu họ làm tôi quấn chúng trong 'sử dụng' ở phạm vi họ cần phải sống. Nó không có gì cho mã hiện tại nhưng tôi chỉ nghĩ rằng tôi muốn đề cập đến nó. –

+0

Hãy xem bài đăng này, nơi bạn có thể sử dụng phân tích mã Visual Studio để phát hiện các vấn đề iDisposable tại thời gian biên dịch: http://stackoverflow.com/a/6213977/2862 –

Trả lời

11

Bạn không tìm kiếm đủ mạnh. Có rất nhiều .NET Profilers bộ nhớ trên đó sẽ xem xét chương trình của bạn khi nó chạy và cho bạn biết nơi/làm thế nào bộ nhớ của bạn được sử dụng (và những gì đang rò rỉ nó).

tôi sẽ kiểm tra bất cứ điều nào sau đây:

Microsoft's CLR Memory Profiler (free)
RedGate ANTS Memory Profiler
JetBrain's DotTrace (includes code profiler as well)
SciTech .NET Memory Profiler

Cập nhật

NET Memory Profiler SciTech của có một tính năng gọi là 'Vứt bỏ Tracker 'phù hợp với dự luật của OP yêu cầu theo dõi chỉ các cuộc gọi Dispose trong ứng dụng của họ.

+0

Điều đó có phát hiện 'Vứt bỏ 'hay chỉ bộ nhớ? Điều gì sẽ xảy ra nếu đối tượng 'IDisposable' của tôi chỉ có' ​​Console.WriteLine ("baz"); '(ví dụ kém, tôi biết, nhưng bạn nhận được điểm) và tôi muốn chắc chắn rằng nó thực sự được gọi là KHÔNG bởi GC? – kizzx2

+0

@ kizzx2 - Nó sẽ phát hiện mọi thứ, nhưng từ đó bạn có thể thu hẹp nó xuống để tìm những gì bạn đang tìm kiếm. –

+0

Tôi đã có những trải nghiệm tốt nhất với trình thu thập bộ nhớ ANTS của RedGate. –

3

bạn có thể làm điều đó, bằng cách thêm Trình kết thúc vào đối tượng IDisposable của bạn. Trong finalizer, bạn có thể kiểm tra xem đối tượng đã được xử lý hay chưa. Nếu nó đã không được xử lý, bạn có thể khẳng định điều này, hoặc viết một cái gì đó để đăng nhập, hoặc bất cứ điều gì.

~Disposable() 
{ 
#if DEBUG 
      // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been 
      // disposed by the programmer. 

      if(_disposed == false) 
      { 
       System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType().Name)); 
      } 
#endif 
      Dispose (false); 
} 

Bạn có thể yếu tố chức năng này thành một lớp cơ sở - Disposable - ví dụ, có thể được sử dụng như một mẫu để thực hiện mô hình Disposable ví dụ.

Như thế này, ví dụ:

/// <summary> 
    /// Abstract base class for Disposable types.  
    /// </summary> 
    /// <remarks>This class makes it easy to correctly implement the Disposable pattern, so if you have a class which should 
    /// be IDisposable, you can inherit from this class and implement the DisposeManagedResources and the 
    /// DisposeUnmanagedResources (if necessary). 
    /// </remarks> 
    public abstract class Disposable : IDisposable 
    { 
     private bool     _disposed = false; 

     /// <summary> 
     /// Releases the managed and unmanaged resources. 
     /// </summary> 
     public void Dispose() 
     { 
      Dispose (true); 
      GC.SuppressFinalize (this); 
     } 

     /// <summary> 
     /// Releases the unmanaged and managed resources. 
     /// </summary> 
     /// <param name="disposing">When disposing is true, the managed and unmanaged resources are 
     /// released. 
     /// When disposing is false, only the unmanaged resources are released.</param> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] 
     protected void Dispose(bool disposing) 
     { 
      // We can suppress the CA1063 Message on this method, since we do not want that this method is 
      // virtual. 
      // Users of this class should override DisposeManagedResources and DisposeUnmanagedResources. 
      // By doing so, the Disposable pattern is also implemented correctly. 

      if(_disposed == false) 
      { 
       if(disposing) 
       { 
        DisposeManagedResources(); 
       } 
       DisposeUnmanagedResources(); 

       _disposed = true; 
      } 
     } 

     /// <summary> 
     /// Override this method and implement functionality to dispose the 
     /// managed resources. 
     /// </summary> 
     protected abstract void DisposeManagedResources(); 

     /// <summary> 
     /// Override this method if you have to dispose Unmanaged resources. 
     /// </summary> 
     protected virtual void DisposeUnmanagedResources() 
     { 
     } 

     /// <summary> 
     /// Releases unmanaged resources and performs other cleanup operations before the 
     /// <see cref="Disposable"/> is reclaimed by garbage collection. 
     /// </summary> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] 
     ~Disposable() 
     { 
#if DEBUG 
      // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been 
      // disposed by the programmer. 

      if(_disposed == false) 
      { 
       System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType().Name)); 
      } 
#endif 
      Dispose (false); 
     } 
    } 
+0

Đó là những gì Raymond tại Old New Thing [gợi ý] (http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx). Tôi sử dụng nó mọi lúc cho các lớp học của riêng mình, bây giờ tôi đã nghĩ rằng nếu tôi cũng có thể bắt được, ví dụ: 'FileStream'. – kizzx2

+0

đây là một cách tiếp cận nghèo nàn vì nó không thể duy trì và thêm quá nhiều sự lộn xộn và phức tạp. Sử dụng một profiler thay thế. Tôi sử dụng Red Gate Memory Profiler mọi lúc. –

+0

Tại sao nó là một cách tiếp cận kém? Tại sao nó không thể duy trì được, và làm tăng thêm sự phức tạp? Nếu bạn yếu tố này mã vào một lớp cơ sở, được gọi là 'dùng một lần' ví dụ, tôi thấy không có vấn đề. Bên cạnh đó, bạn có thể sử dụng lớp cơ sở này làm 'mẫu' để triển khai chính xác 'Mẫu dùng một lần'. –

1

Trong khi giới thiệu @Justin Niessner của hoạt động, tôi thấy rằng việc sử dụng một hồ sơ đầy đủ thổi quá nặng.

Tôi đã tạo giải pháp pha chế tại nhà: EyeDisposable. Nó tập hợp các công cụ để phát hiện khi Dispose chưa được gọi.

+0

Cảm ơn, trông khá tuyệt! Tôi đã yêu thích * tĩnh * phân tích cho điều này mặc dù (có lẽ là một plugin R #). Một chú thích nhỏ, trong phần * Cloning * của readme người ta phải chạy 'git submodule init' trước' git submodule update'. –

+1

@OhadSchneider Cảm ơn những người đứng đầu - phân tích tĩnh sẽ được mát mẻ nhưng bị ràng buộc có nhiều tích cực sai cho các trường hợp không tầm thường - và là cách trung thực quá phức tạp hơn so với phạm vi ban đầu của tiện ích _small_ này: P – kizzx2

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