2008-12-05 28 views
6

Tôi có một dịch vụ bị rò rỉ bộ nhớ rất chậm. Nếu tôi phân tích các bộ đếm .NET CLR Đang tải, tôi thấy rằng các lớp hiện tại Các lớp hiện tại được tải không ngừng tăng lên và phù hợp với Tổng số lớp được nạp bộ đếm mọi lúc. Điều này mang lại cho tôi ấn tượng rằng sự rò rỉ bộ nhớ có liên quan đến các nguồn không được giải phóng (Đây chỉ là một phỏng đoán).Các lớp hiện tại được tải liên tục tăng - Bộ nhớ rò rỉ

Dịch vụ tạo các appDomain mới mỗi lần thực hiện tác vụ (kiến trúc trình cắm thêm).

Tôi cần tìm ra tên lớp để tôi có thể thu hẹp nguyên nhân rò rỉ. Tôi không thành thạo với WinDbg, nhưng tôi đã tự hỏi liệu có ai có thể hướng dẫn tôi qua các bước để liệt kê các lớp được tải này không.

Tôi có mã nguồn để tôi có thể lấy các tệp biểu tượng nếu cần. Cảm ơn trước sự giúp đỡ nào!

+1

Bạn có phá hủy AppDomain mới mỗi lần sau khi sử dụng không? –

+0

Có, AppDomain.Unload được gọi. Ngoài ra, các bộ đếm hiệu suất cho AppDomains không liên tục tăng lên. –

Trả lời

2

Tôi tin rằng vấn đề thực sự là do một loạt các phiên bản FileSystemWatcher chưa được xử lý đã được lồng vào bên trong MBT của RemoteTaskRunner. Tôi vẫn không chắc mình đã giải quyết hoàn toàn rò rỉ bộ nhớ, nhưng tôi chắc chắn có thể nói một sự khác biệt.

Dường như đây không phải là lần đầu tiên FileSystemWatchers gây ra sự cố cho tôi. :)

Cảm ơn tất cả mọi người (đặc biệt là leppie) vì đã giúp tôi với điều này!

1

Tôi khuyên bạn nên sử dụng một trình biên tập bộ nhớ thích hợp - như ".NET Memory Profiler" - http://memprofiler.com/ Bạn chắc chắn có thể dùng thử để đánh giá xem đó có phải là công cụ hữu ích hay không.

Điều đó sẽ cho phép bạn xem tất cả các đối tượng trực tiếp dễ dàng hơn nhiều so với công cụ phần cứng WinDBG/SoS.

0

EDIT: Sau khi đọc một số bài đăng khác, chúng nên được xem xét sau khi sử dụng profiler tốt hơn và sau khi vấn đề appDomain được sắp xếp.

Bạn có thể muốn thêm bộ đếm hiệu suất vào dịch vụ của mình để ít nhất các đối tượng bạn tạo có thể được theo dõi. Điều đó sẽ giúp bạn xác định xem Classes.Loaded là của bạn hay các lớp CLR.

Đồng thời thêm một số lần đăng nhập gỡ lỗi khi bạn tạo và phá hủy đối tượng của mình một cách rõ ràng có thể hữu ích.

Như một phương sách cuối cùng, bạn có thể thử đặt GC.Collect() vào đó để xem liệu điện thoại có khắc phục được sự cố của bạn hay không. Nó không phải sửa lỗi, nhưng kiểm tra nó sẽ cho bạn biết đó là một tùy chọn.

2

Đây có phải là .net 2.0 trở lên không? Nếu vậy, bạn có thể không dỡ bỏ AppDomain (như Jon Skeet đã nói trong phần bình luận).

Nếu phiên bản 1.1 trở xuống, có lỗi trong chức năng dỡ tải AppDomain. I E. nó không giải phóng bộ nhớ hoặc giải phóng tài nguyên khi AppDomain bị "tải".

(Điều này đã được sửa thành .net 2.0)

+0

Tôi đồng ý, chúng sẽ được dỡ xuống. – leppie

1

Như đã nói, câu trả lời khác, AppDomain.Unload() nên được sử dụng.

Tuy nhiên, bạn cần phải cẩn thận bạn không tải cụm ở nhiều nơi, đặc biệt là tên miền ứng dụng chính.

Xem tài liệu MSDN cho AppDomain.Load (AssemblyName) để biết giải thích về cách xảy ra ở trên.

Cũng trong cùng một dòng, bạn có chắc là bạn đang sử dụng các lớp học có thể truy cập từ xa thích hợp không? Nếu không, điều trên chắc chắn sẽ xảy ra.

0

AppDomains được tải xuống, nhưng phản hồi từ leppie, làm tôi băn khoăn liệu các hội đồng plugin có đang được tải vào cả AppDomain chính và AppDomain phụ hay không. Khi tôi nhìn vào các quầy hiệu suất, số lượng AppDomain hiện tại không liên tục tăng lên.

Ứng dụng được cho là tạo Miền phụ appDomain rồi tải một trình cắm plugin riêng biệt. Có lẽ một số mã sẽ giúp:

Tạo AppDomain thứ cấp từ AppDomain chính:

AppDomainSetup ads = new AppDomainSetup(); 
ads.ApplicationName = "RemoteAgentLib"; 
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; 
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory; 
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory; 
ads.ShadowCopyFiles = "true"; 

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads); 

Sử dụng RemoteTaskRunner để nạp plugin trong AppDomain thứ:

RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
        Assembly.GetExecutingAssembly().FullName, 
        typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner; 
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type); 

Sử dụng RemoteTaskRunner để Execute công việc trong ứng dụng phụDomain:

[Serializable] 
internal class RemoteTaskRunner : MarshalByRefObject 
{ 
    private ITask m_task; 

    public RemoteTaskRunner() 
    { 
    } 

    internal void LoadTask(string assembly, string type) 
    { 
     // This assembly should load in the secondary appDomain. 
     Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly); 
     m_task = taskAssembly.CreateInstance(type) as ITask; 
    } 

    internal void RunTask(string taskConfig) 
    { 
     // This method should run in the secondary appDomain. 
     m_task.RunTask(taskConfig, m_logger); 
    } 
... 
... 

To thực hiện nhiệm vụ plugin, các dòng mã sau đây được sử dụng trong AppDomain chính:

taskRunner.RunTask(taskInfo.TaskConfig); 

Sau khi kết thúc nhiệm vụ, AppDomain được bốc dỡ:

AppDomain.Unload(m_domain); 
+0

Bạn chỉ có thể đổ tất cả các hội đồng được nạp vào tên miền chính và xem liệu chúng có được tải không chính xác bằng cách nào đó không. Điều này có thể rất phức tạp. – leppie

+0

BTW, liệu MarshalByRefObject của bạn có thể được tuần tự hóa không? Bạn có thể xác minh, sau khi tạo nó, rằng nó thực sự là một proxy trong suốt? – leppie

+0

Thuộc tính Serializable dường như không có ảnh hưởng đến việc thực thi. Cả hai có và không có RemoteTaskRunner là một Proxy trong suốt. –

1

tôi chỉ đọc trên blog của Suzanne Cook.

http://blogs.msdn.com/suzcook/archive/2003/06/12/57169.aspx

Hãy chắc chắn để không vượt qua bất kỳ Loại/hội/etc. các trường hợp (bên cạnh loại MarshalByRefObject của bạn) quay lại tên miền ứng dụng gốc. Nếu bạn làm như vậy, nó sẽ khiến các hội đồng đó được tải vào appdomain gốc. Nếu cài đặt tên miền ứng dụng khác nhau giữa hai tên miền ứng dụng, những cụm từ đó có thể không tải được ở đó. Ngoài ra, ngay cả khi chúng được tải thành công , các hội đồng sẽ vẫn giữ được tải và bị khóa sau khi mục tiêu tên miền ứng dụng được tải xuống, ngay cả khi tên miền ứng dụng ban đầu không bao giờ sử dụng chúng.

Khi cô ấy nói bất kỳ loại/hội/v.v. Cô ấy có thể BẤT K type loại gì? Lý do tôi hỏi, là vì MarshalByRefObject (RemoteTaskRunner) của tôi trả về một đối tượng DateTime sau khi nhiệm vụ chạy. Điều này có thể gây ra các hội đồng plugin để được tải vào appDomain chính của tôi (và cuối cùng gây ra rò rỉ bộ nhớ)?

+0

Không, một DateTime là tốt, vì hội đồng mscorlib đã được nạp. – leppie

2

Bạn luôn có thể kiểm tra những bộ phận nhỏ được nạp trong AppDomain của bạn:

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{ 
     Console.WriteLine(assembly.FullName); 
} 

Vì vậy, nếu bạn vô tình tải các hội đồng trong lĩnh vực sai nó wont được cứng quá xem.

Edit:

nếu bạn muốn sử dụng WinDgb SOS, here là các lệnh được hỗ trợ. Bạn có nhiều khả năng quan tâm đến: DumpDomain, DumpClass, DumpAssembly, EEHeap ...

1

Tôi luôn luôn ném nó ra khỏi đó, bất cứ khi nào ai đó báo cáo rò rỉ bộ nhớ, bởi vì nó giữ tôi bận rộn trong một vài tuần. Không chạy ứng dụng của bạn ở chế độ gỡ lỗi. Nếu bạn chạy ứng dụng của mình ở chế độ gỡ lỗi trong .Net 2.0+ (không xảy ra trong .Net 1.1) và bạn khởi tạo lớp có chứa sự kiện, ngay cả khi bạn không tăng sự kiện, nó sẽ chỉ giữ một mảnh bộ nhớ. Điều này rất có thể ảnh hưởng đến các ứng dụng đang chạy lâu dài, chẳng hạn như các dịch vụ và ứng dụng web, bởi vì theo thời gian, lượng bộ nhớ nhỏ đã ăn mòn sau khi instantiating các đối tượng có thể tăng lên khá nhiều.

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