2012-06-21 8 views
7

Tôi đang làm việc trên ứng dụng .NET có vẻ như bị rò rỉ bộ nhớ. Tôi biết câu trả lời trong sách giáo khoa, các sự kiện đó phải được hủy đăng ký, các đối tượng dùng một lần nên được xử lý ...Gỡ lỗi rò rỉ bộ nhớ .NET - làm thế nào để biết những gì đang nắm giữ một tham chiếu đến những gì?

Tôi có một dây nịt thử nghiệm có thể tái tạo lỗi. Trong finalizer của một lớp nào đó tôi viết để an ủi

public class Foo 
{ 
    // Ctor 
    public Foo() 
    { 
    } 

    ~public Foo() 
    { 
     Console.WriteLine("Foo Finalized"); 
    } 
} 

trong khai thác thử nghiệm, tôi đang tạo ra một trường hợp duy nhất của Foo (mà lần lượt tạo ra và tương tác với hàng trăm loại khác) sau đó loại bỏ nó và viện dẫn Thu gom rác.

Tôi tìm thấy Trình gỡ lỗi Foo không bao giờ được gọi. Tôi có một lớp học tương tự với thiết lập này được hoàn thành như một bài kiểm tra kiểm soát.

Vì vậy, câu hỏi của tôi là thế này:

Làm thế nào tôi có thể xác định bằng các công cụ mã nguồn thương mại hoặc mở chính xác những gì đang nắm giữ một tham chiếu đến Foo?

Tôi có giấy phép chuyên nghiệp cho trình thu thập thông tin bộ nhớ dotTrace nhưng không thể tìm ra từ các tệp trợ giúp về cách sử dụng nó.

Cập nhật: Tôi hiện đang sử dụng dotMemory 4.0, là bộ kế thừa cho bộ nhớ dotTrace (tốt, nhưng không thể sử dụng) 3.5.

+1

nếu bạn đã có trình thu thập thông tin học cách sử dụng nó - Tôi không làm việc với dotTrace rất nhiều nhưng tôi hai khác tương tự trong đó sử dụng các mẫu và cần một số "tham gia" quá – Carsten

+0

@ CarstenKönig +1 cho điều này, Tôi đang đào xung quanh trong dotTrace ngay bây giờ. Tìm ra tôi có thể tìm thấy một danh sách các đối tượng uncollected không gian tên (do đó tìm thấy Foo vi phạm của tôi) và những gì giữ một tham chiếu đến chúng, một lần nữa lọc theo không gian tên (hàng trăm), bây giờ đi qua một danh sách xử lý sự kiện đáng ngờ vv ... –

+0

@ CarstenKönig Trong thực tế, tìm thấy nó bằng cách sử dụng dotTrace! Nó có tính năng để đi sâu vào đồ thị từ gcroot -> Foo, chỉ là một chút khó khăn để tìm. Chúc mừng :) –

Trả lời

3

Các finalizer không deterministically gọi, vì vậy hãy cẩn thận khi sử dụng nó để theo dõi mọi thứ theo một cách đáng tin cậy. Nếu bạn loại bỏ finalizer và thay vì sử dụng một WeakReference<Foo> bạn sẽ có thể xác định xem đối tượng đã được thu thập.

Tất cả các trình thu thập bộ nhớ sẽ có thể tìm thấy vấn đề như vậy, nhưng với mức độ khó khác nhau. Cá nhân tôi đã sử dụng ANTS rất dễ sử dụng, nhưng không miễn phí. Nó sẽ giúp bạn hiển thị một sơ đồ tham chiếu đến cá thể Foo, tất cả các cách từ đối tượng gốc GC. Thấy sơ đồ này, người ta thường dễ dàng nhận ra ai đang giữ tham chiếu.

2

Bạn có thể sử dụng trình thu thập bộ nhớ để xác định rò rỉ bộ nhớ. Dưới đây là một số người,

MemProfiler

ANTS Profiler

+0

Đã bỏ phiếu cho ANTS Profiler. Trong một ứng dụng doanh nghiệp hợp lý lớn, quản lý để tìm một rò rỉ tài nguyên khổng lồ đã thoát khỏi phát hiện trong 3 năm. – Contango

6

Có một số look tại số máy lẻ SOS debugger (miễn phí, có thể được sử dụng trong Visual Studio).

Bạn có thể tìm thấy thisthis hữu ích để bắt đầu.

Nếu bạn đã succefully lập SOS (điều này có thể được khôn lanh đôi khi), biết những gì giữ một tham chiếu đến những gì là dễ dàng như

// load sos 
.load sos 
// list of all instances of YourTypeName in memory with their method tables 
!DumpHeap -type YourTypeName 
// put here the method table displayed by the previous command 
// it will show you the memory address of the object 
!DumpHeap -mt 07f66b44    
// displays information about references the object at the specified address 
!GCRoot 02d6ec94 
+0

Cảm ơn vì điều này, tôi chắc chắn sẽ thử. Sẽ được tốt đẹp nếu có một hướng dẫn tốt về dotTrace nhưng đáng buồn là không! –

1

Trước hết bạn không nên sử dụng một finalizer, bởi vì:

hoạt động Finalize có những hạn chế sau:

  • thời gian chính xác khi finalizer thực hiện trong thùng rác bộ sưu tập không xác định. Tài nguyên không được đảm bảo sẽ được phát hành tại bất kỳ thời điểm cụ thể nào trừ khi gọi phương thức Đóng hoặc phương thức Vứt bỏ.

  • Các finalizers của hai đối tượng không được đảm bảo để chạy trong bất kỳ thứ tự cụ thể nào, ngay cả khi một đối tượng đề cập đến một đối tượng khác. Tức là, nếu Đối tượng A có tham chiếu đến Object B và cả hai đều có finalizers, Object B có thể đã hoàn thành khi finalizer của Object A bắt đầu.

  • Chủ đề mà trình chạy finalizer được chạy không được chỉ định.

Trích dẫn từ: http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx
tôi sẽ đề nghị sử dụng phương thức Dispose để thay thế.

Thứ hai, bất kỳ trình thu thập bộ nhớ nào cũng có thể tìm thấy những gì chứa các tham chiếu đó. Cá nhân tôi đã sử dụng ANTS Profiler, nó là một công cụ rất đẹp và có tài liệu khá phong phú. Bạn có thể thử đọc tài liệu này: http://downloads.red-gate.com/HelpPDF/ANTS_Memory_Profiler/InstanceCategorizer.pdf Trình phân loại thể hiện hiển thị chuỗi tham chiếu từ bộ đối tượng đến gốc GC.

+0

Trong khi bạn hoàn toàn đúng, trong một số trường hợp nó thực sự cần thiết để sử dụng Finalizer. Hãy xem xét bạn đã tạo một điều khiển tùy chỉnh để hiển thị các bề mặt 3D mà bạn phân phối lại cho người dùng cuối. Người dùng cuối không thể gọi .Dispose() trên điều khiển của bạn khi nó không còn được sử dụng nữa. Họ hy vọng nó sẽ 'làm việc'. Ngoài ra WPF rác thu thập quan điểm đôi khi khi chúng được templated và trong một tab kiểm soát và tabbed ra và in Trong những trường hợp này không thể gọi .Dispose và Finalizer là lựa chọn tốt nhất để phát hành tài nguyên không được quản lý bằng cách gọi trực tiếp Vứt bỏ cho bạn, nếu chưa –

6

Gỡ lỗi rò rỉ bộ nhớ có thể là quá trình tham gia khá và đòi hỏi sự hiểu biết thấu đáo về logic chương trình của bạn và ít nhất một số nội bộ .Net (đặc biệt là hành vi thu gom rác).

Để biết thêm thông tin xem các liên kết sau đây:

Tốt giới thiệu

Hands-on khóa học:

GC và.internals Net

WinDbg với phần mở rộng SOS

Chúc may mắn!