Những chiến lược và công cụ nào hữu ích cho việc tìm kiếm rò rỉ bộ nhớ trong .NET?


152

Tôi đã viết C ++ trong 10 năm. Tôi gặp phải vấn đề về bộ nhớ, nhưng chúng có thể được sửa chữa với một nỗ lực hợp lý.

Trong vài năm qua tôi đã viết C #. Tôi thấy tôi vẫn còn nhiều vấn đề về bộ nhớ. Chúng rất khó chẩn đoán và khắc phục do không xác định và vì triết lý C # là bạn không nên lo lắng về những điều như vậy khi bạn chắc chắn làm.

Một vấn đề đặc biệt tôi tìm thấy là tôi phải xử lý và dọn dẹp mọi thứ trong mã một cách rõ ràng. Nếu tôi không làm vậy, thì trình biên dịch bộ nhớ không thực sự hữu ích vì có quá nhiều chuyện vặt vãnh về bạn không thể tìm thấy rò rỉ trong tất cả dữ liệu họ đang cố gắng hiển thị cho bạn. Tôi tự hỏi nếu tôi có ý tưởng sai, hoặc nếu công cụ tôi có không phải là tốt nhất.

Loại chiến lược và công cụ nào hữu ích để khắc phục rò rỉ bộ nhớ trong .NET?


Tiêu đề của bài đăng của bạn không thực sự phù hợp với câu hỏi trong bài viết của bạn. Tôi đề nghị bạn cập nhật tiêu đề của bạn.
Kevin

Bạn đúng. Xin lỗi, tôi đã chán ngấy với sự rò rỉ hiện tại tôi đang săn lùng! Tiêu đề cập nhật.
Scott Langham

3
@Scott: Đừng chán với .NET, đó không phải là vấn đề. Mã của bạn là.
GEOCHET

3
Đúng, mã của tôi hoặc thư viện của bên thứ ba mà tôi có niềm vui khi sử dụng.
Scott Langham

@ Hủy bỏ: Xem câu trả lời của tôi. MemProfiler là giá trị nó. Sử dụng nó cũng sẽ cung cấp cho bạn một mức độ hiểu biết hoàn toàn mới về thế giới .NET GC.
GEOCHET

Câu trả lời:


51

Tôi sử dụng MemProfiler của Scitech khi tôi nghi ngờ rò rỉ bộ nhớ.

Cho đến nay, tôi đã tìm thấy nó rất đáng tin cậy và mạnh mẽ. Nó đã cứu thịt xông khói của tôi ít nhất một lần.

GC hoạt động rất tốt trong .NET IMO, nhưng cũng giống như bất kỳ ngôn ngữ hoặc nền tảng nào khác, nếu bạn viết mã xấu, điều tồi tệ sẽ xảy ra.


3
Đúng, tôi đã đi với cái này, và nó đã giúp tôi đi đến tận cùng của một số rò rỉ khó khăn. Các rò rỉ lớn nhất mà tôi đã gây ra là do các thư viện bên thứ ba gây ra trong mã không được quản lý mà họ truy cập thông qua interop. Tôi rất ấn tượng rằng công cụ này đã phát hiện rò rỉ trong mã không được quản lý cũng như mã được quản lý.
Scott Langham

1
Tôi đã chấp nhận đây là câu trả lời vì cuối cùng nó cũng có tác dụng với tôi, nhưng tôi nghĩ tất cả các câu trả lời khác đều rất hữu ích. Nhân tiện, công cụ này thường được gọi là Mem Profiler của SciTech!
Scott Langham

41

Chỉ dành cho vấn đề quên để xử lý, hãy thử giải pháp được mô tả trong bài đăng trên blog này . Đây là bản chất:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif

Tôi muốn ném một ngoại lệ thay vì Debug.Fail
Pedro77

17

Chúng tôi đã sử dụng phần mềm Ants Profiler Pro by Red Gate trong dự án của chúng tôi. Nó hoạt động thực sự tốt cho tất cả các ứng dụng dựa trên ngôn ngữ .NET.

Chúng tôi thấy rằng .NET Garbage Collector rất "an toàn" trong việc dọn sạch các đối tượng trong bộ nhớ (như nó phải vậy). Nó sẽ giữ các vật thể xung quanh chỉ vì chúng ta có thể sẽ sử dụng nó trong tương lai. Điều này có nghĩa là chúng ta cần phải cẩn thận hơn về số lượng đối tượng mà chúng ta bị thổi phồng trong bộ nhớ. Cuối cùng, chúng tôi đã chuyển đổi tất cả các đối tượng dữ liệu của mình thành "tăng theo yêu cầu" (ngay trước khi một trường được yêu cầu) để giảm chi phí bộ nhớ và tăng hiệu suất.

EDIT: Đây là một lời giải thích thêm về những gì tôi có nghĩa là "tăng theo yêu cầu." Trong mô hình đối tượng của cơ sở dữ liệu của chúng tôi, chúng tôi sử dụng Thuộc tính của đối tượng cha để hiển thị (các) đối tượng con. Ví dụ: nếu chúng tôi có một số bản ghi tham chiếu một số bản ghi "chi tiết" hoặc "tra cứu" khác trên cơ sở một-một, chúng tôi sẽ cấu trúc nó như sau:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Chúng tôi thấy rằng hệ thống trên đã tạo ra một số vấn đề về bộ nhớ và hiệu năng thực khi có rất nhiều bản ghi trong bộ nhớ. Vì vậy, chúng tôi đã chuyển sang một hệ thống nơi các đối tượng chỉ bị thổi phồng khi chúng được yêu cầu và các cuộc gọi cơ sở dữ liệu chỉ được thực hiện khi cần thiết:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Điều này hóa ra hiệu quả hơn nhiều vì các đối tượng được giữ ngoài bộ nhớ cho đến khi chúng cần thiết (phương thức Get được truy cập). Nó cung cấp một sự gia tăng hiệu suất rất lớn trong việc hạn chế các lần truy cập cơ sở dữ liệu và tăng rất nhiều dung lượng bộ nhớ.


Tôi thứ hai sản phẩm này. Đó là một trong những hồ sơ tốt nhất mà tôi đã sử dụng.
Gord

Tôi thấy trình hồ sơ khá tốt để xem xét các vấn đề về hiệu suất. Tuy nhiên, các công cụ phân tích bộ nhớ khá kém. Tôi đã tìm thấy một rò rỉ với công cụ này, nhưng nó thật rác rưởi trong việc giúp tôi xác định nguyên nhân rò rỉ. Và nó hoàn toàn không giúp bạn nếu rò rỉ xảy ra ở mã không được quản lý.
Scott Langham

Ok, phiên bản mới 5.1, tốt hơn rất nhiều. Tốt hơn là giúp bạn tìm ra nguyên nhân rò rỉ (mặc dù - vẫn còn một số vấn đề với ANTS đã nói với tôi rằng họ sẽ khắc phục trong phiên bản tiếp theo). Vẫn không làm mã không được quản lý, nhưng nếu bạn không bận tâm về mã không được quản lý, thì đây là một công cụ khá tốt.
Scott Langham

7

Bạn vẫn cần phải lo lắng về bộ nhớ khi bạn đang viết mã được quản lý trừ khi ứng dụng của bạn không quan trọng. Tôi sẽ đề xuất hai điều: đầu tiên, đọc CLR qua C # vì nó sẽ giúp bạn hiểu quản lý bộ nhớ trong .NET. Thứ hai, học cách sử dụng một công cụ như CLRProfiler (Microsoft). Điều này có thể cho bạn ý tưởng về nguyên nhân gây rò rỉ bộ nhớ của bạn (ví dụ: bạn có thể xem phân mảnh đống đối tượng lớn của mình)


Vâng. CLRPRofiler là khá mát mẻ. Nó có thể có một chút bùng nổ với thông tin khi cố gắng tìm hiểu qua chế độ xem nó cung cấp cho bạn các đối tượng được phân bổ, nhưng mọi thứ đều ở đó. Đó chắc chắn là một điểm khởi đầu tốt, đặc biệt là miễn phí.
Scott Langham

6

Bạn đang sử dụng mã không được quản lý? Nếu bạn không sử dụng mã không được quản lý, theo Microsoft, rò rỉ bộ nhớ theo nghĩa truyền thống là không thể.

Tuy nhiên, bộ nhớ được sử dụng bởi một ứng dụng có thể không được phát hành, do đó việc cấp phát bộ nhớ của ứng dụng có thể tăng lên trong suốt vòng đời của ứng dụng.

Từ Cách xác định rò rỉ bộ nhớ trong thời gian chạy ngôn ngữ phổ biến tại Microsoft.com

Rò rỉ bộ nhớ có thể xảy ra trong ứng dụng .NET Framework khi bạn sử dụng mã không được quản lý như một phần của ứng dụng. Mã không được quản lý này có thể rò rỉ bộ nhớ và thời gian chạy .NET Framework không thể giải quyết vấn đề đó.

Ngoài ra, một dự án chỉ có thể bị rò rỉ bộ nhớ. Điều kiện này có thể xảy ra nếu nhiều đối tượng lớn (như đối tượng DataTable) được khai báo và sau đó được thêm vào bộ sưu tập (chẳng hạn như Bộ dữ liệu). Các tài nguyên mà các đối tượng này sở hữu có thể không bao giờ được phát hành và các tài nguyên được giữ nguyên cho toàn bộ chương trình. Điều này dường như là một rò rỉ, nhưng thực sự nó chỉ là một triệu chứng của cách bộ nhớ được phân bổ trong chương trình.

Để xử lý loại vấn đề này, bạn có thể triển khai IDis Dùng một lần . Nếu bạn muốn xem một số chiến lược để xử lý việc quản lý bộ nhớ, tôi khuyên bạn nên tìm kiếm IDis Dùng, XNA, quản lý bộ nhớ vì các nhà phát triển trò chơi cần phải có bộ sưu tập rác có thể dự đoán được nhiều hơn và do đó phải buộc GC phải làm việc đó.

Một lỗi phổ biến là không xóa trình xử lý sự kiện đăng ký vào một đối tượng. Một thuê bao xử lý sự kiện sẽ ngăn chặn một đối tượng được tái chế. Ngoài ra, hãy xem câu lệnh sử dụng cho phép bạn tạo phạm vi giới hạn cho vòng đời của tài nguyên.


5
Xem blog.msdn.com/tess/archive/2006/01/23/ . Nó không thực sự quan trọng cho dù rò rỉ bộ nhớ là "truyền thống" hay không, nó vẫn là một rò rỉ.
Constantin

2
Tôi thấy quan điểm của bạn - nhưng việc phân bổ và tái sử dụng bộ nhớ không hiệu quả bởi một chương trình khác với rò rỉ bộ nhớ.
Timothy Lee Russell

câu trả lời tốt, cảm ơn bạn đã nhớ tôi rằng xử lý sự kiện có thể nguy hiểm.
frameworkninja

3
@Timothy Lee Russel: Nếu một lượng bộ nhớ không bị chặn (1) có thể vẫn được phân bổ đồng thời (đã root) sau khi trở nên vô dụng (2), mà không có bất cứ điều gì trong hệ thống có thông tin và động lực cần thiết để giải phóng nó kịp thời, đó là rò rỉ bộ nhớ . Ngay cả khi bộ nhớ có thể được giải phóng vào một ngày nào đó, nếu đủ thứ vô dụng có thể tích lũy để bóp nghẹt hệ thống trước khi điều đó xảy ra, đó là một sự rò rỉ. (1) Lớn hơn O (N), N là lượng phân bổ hữu ích; (2) Nội dung là vô ích nếu xóa tham chiếu đến nó sẽ không ảnh hưởng đến chức năng của chương trình.
supercat

2
@Timothy Lee Russel: Mẫu "rò rỉ bộ nhớ" bình thường xảy ra khi bộ nhớ được giữ bởi một thực thể thay mặt cho một thực thể khác , hy vọng sẽ được thông báo khi không còn cần thiết nữa, nhưng sau đó từ bỏ thực thể mà không nói với thực thể đầu tiên. Thực thể giữ bộ nhớ không thực sự cần nó, nhưng không có cách nào xác định điều đó.
supercat

5

Blog này có một số hướng dẫn thực sự tuyệt vời bằng cách sử dụng Windbg và các công cụ khác để theo dõi rò rỉ bộ nhớ của tất cả các loại. Đọc tuyệt vời để phát triển kỹ năng của bạn.


5

Tôi vừa bị rò rỉ bộ nhớ trong một dịch vụ windows, mà tôi đã sửa.

Đầu tiên, tôi đã thử MemProfiler . Tôi thấy nó thực sự khó sử dụng và hoàn toàn không thân thiện với người dùng.

Sau đó, tôi đã sử dụng JustTrace dễ sử dụng hơn và cung cấp cho bạn nhiều chi tiết hơn về các đối tượng không được xử lý chính xác.

Nó cho phép tôi giải quyết rò rỉ bộ nhớ thực sự dễ dàng.


3

Nếu rò rỉ mà bạn đang quan sát là do triển khai bộ đệm ẩn, đây là một trường hợp mà bạn có thể muốn xem xét việc sử dụng WeakReference. Điều này có thể giúp đảm bảo rằng bộ nhớ được giải phóng khi cần thiết.

Tuy nhiên, IMHO sẽ tốt hơn nếu xem xét một giải pháp bespoke - chỉ bạn thực sự biết bạn cần giữ các đồ vật xung quanh trong bao lâu, vì vậy thiết kế mã vệ sinh phù hợp cho tình huống của bạn thường là cách tiếp cận tốt nhất.


3

Tôi thích dotmemory từ Jetbrains


bạn có thể là người duy nhất :)
HellBaby

Tôi cũng đã thử nó. Tôi nghĩ rằng đây là một công cụ tốt. Dễ sử dụng, nhiều thông tin. Tích hợp vào Visual Studio
redeye

Trong trường hợp của chúng tôi, khi xử lý sự cố rò rỉ bộ nhớ, công cụ Chụp nhanh Visual Studio đã bị sập / không chụp nhanh. Dotmemory giữ cho nó mát mẻ và xử lý nhiều ảnh chụp nhanh hơn 3 GB một cách dễ dàng (dường như).
Michael Kargl

3

Súng lớn - Công cụ gỡ lỗi cho Windows

Đây là một bộ sưu tập tuyệt vời của các công cụ. Bạn có thể phân tích cả đống được quản lý và không được quản lý với nó và bạn có thể thực hiện ngoại tuyến. Điều này rất thuận tiện để gỡ lỗi một trong những ứng dụng ASP.NET của chúng tôi tiếp tục tái chế do lạm dụng bộ nhớ. Tôi chỉ phải tạo một bộ nhớ đầy đủ cho quá trình sống đang chạy trên máy chủ sản xuất, tất cả các phân tích đã được thực hiện ngoại tuyến trong WinDbg. (Hóa ra một số nhà phát triển đã lạm dụng bộ nhớ Phiên trong bộ nhớ.)

"Nếu bị hỏng thì ..." blog có những bài viết rất hữu ích về chủ đề này.


2

Điều tốt nhất cần ghi nhớ là theo dõi các tài liệu tham khảo đến các đối tượng của bạn. Rất dễ dàng để kết thúc với việc treo các tham chiếu đến các đối tượng mà bạn không quan tâm nữa. Nếu bạn sẽ không sử dụng một cái gì đó nữa, hãy loại bỏ nó.

Làm quen với việc sử dụng một nhà cung cấp bộ đệm với thời hạn trượt, do đó, nếu một cái gì đó không được tham chiếu cho một cửa sổ thời gian mong muốn, nó sẽ được hủy đăng ký và làm sạch. Nhưng nếu nó được truy cập nhiều thì nó sẽ ghi vào bộ nhớ.


2

Một trong những công cụ tốt nhất là sử dụng các công cụ gỡ lỗi cho Windows , và chụp một bãi chứa ký ức về quá trình sử dụng adplus , sau đó sử dụng windbgsos cắm để phân tích bộ nhớ quá trình, chủ đề, và ngăn xếp cuộc gọi.

Bạn cũng có thể sử dụng phương pháp này để xác định sự cố trên máy chủ, sau khi cài đặt công cụ, chia sẻ thư mục, sau đó kết nối với chia sẻ từ máy chủ bằng cách sử dụng (sử dụng mạng) và xử lý sự cố hoặc treo kết xuất của quy trình.

Sau đó phân tích nhé.


Có, điều này hoạt động tốt, đặc biệt đối với các công cụ nâng cao hơn hoặc chẩn đoán sự cố trong phần mềm được phát hành mà bạn không thể dễ dàng đính kèm trình gỡ lỗi. Blog này có rất nhiều mẹo để sử dụng tốt các công cụ này: blog.msdn.com/tess
Scott Langham

2

Sau một trong những sửa lỗi của tôi cho ứng dụng được quản lý, tôi cũng gặp phải điều tương tự, như cách xác minh rằng ứng dụng của tôi sẽ không bị rò rỉ bộ nhớ tương tự sau lần thay đổi tiếp theo, vì vậy tôi đã viết một cái gì đó như khung Xác minh Phát hành Đối tượng, vui lòng xem qua gói NuGet ObjectReleaseVerification . Bạn có thể tìm thấy một mẫu ở đây https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerifying-Sample và thông tin về mẫu này http://outcoldman.ru/en/blog/show/322


0

Từ Visual Studio 2015 xem xét sử dụng công cụ chẩn đoán Bộ nhớ sử dụng ngoài hộp để thu thập và phân tích dữ liệu sử dụng bộ nhớ.

Công cụ sử dụng bộ nhớ cho phép bạn chụp một hoặc nhiều ảnh chụp nhanh của bộ nhớ gốc được quản lý và bộ nhớ gốc để giúp hiểu tác động sử dụng bộ nhớ của các loại đối tượng.


0

một trong những công cụ tốt nhất tôi đã sử dụng DotMemory. Bạn có thể sử dụng công cụ này làm tiện ích mở rộng trong VS. Sau khi chạy ứng dụng của bạn, bạn có thể phân tích mọi phần của bộ nhớ (bằng Object, NameSpace, v.v.) mà ứng dụng của bạn sử dụng và chụp một số ảnh chụp nhanh về điều đó , So sánh nó với SnapShots khác. DotMemory

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.