2009-11-05 29 views
6

Đây là phần mềm phức tạp nhất mà tôi đã xây dựng và bây giờ có vẻ như nó sắp hết bộ nhớ tại một số điểm. Tôi chưa thực hiện thử nghiệm rộng rãi, bởi vì tôi hơi lạc mất cách tiếp cận vấn đề này.Ứng dụng (lớn) của tôi ném ra một OutOfMemoryException, bây giờ là gì?

HandleCount: 277 
NonpagedSystemMemorySize: 48136 
PagedMemorySize: 1898590208 
PagedSystemMemorySize: 189036 
PeakPagedMemorySize: 1938321408 
VirtualMemorySize: 2016473088 
PeakVirtualMemory: 2053062656 
WorkingSet: 177774592 
PeakWorkingSet: 883834880 
PrivateMemorySize: 1898590208 
PriviligedProcessorTime: 00:00:15.8593750 
UserProcessorTime: 00:00:01.6562500 
TotalProcessorTime: 00:00:17.5156250 
GDI Objects: 30 
User Objects: 27 

Tôi có một catcher tự động toàn cầu ngoại trừ rằng khi ngoại lệ tập hợp các thông tin trên (sử dụng System.Diagnostics.Process) - cùng với các thông tin ngoại lệ, đăng nhập và một ảnh chụp màn hình - và e-mail cho tôi tất cả mọi thứ.

Điều này đã hoạt động tốt khi tôi có thể cắm các lỗi dựa trên thông tin được gửi qua email. Đây là, cho đến bây giờ. Phần mềm này là hàng chục nghìn dòng và sử dụng các tài nguyên được quản lý và không được quản lý.

Tôi có thể bắt đầu xem xét mã, từng dòng, nhưng một số cách tôi cảm thấy đây có thể không phải là cách tiếp cận tốt nhất để cố gắng suy luận vấn đề xây dựng bộ nhớ.

Vì trước đây tôi chưa bao giờ thực hiện loại phân tích này, bạn đề xuất cách tiếp cận loại vấn đề này như thế nào?

Trả lời

2

Đính kèm trình gỡ lỗi vào đó và tạo lại lỗi. Ngăn xếp cuộc gọi vào thời điểm ngoại lệ sẽ cho bạn biết lỗi ở đâu.

Hoặc bạn có một rò rỉ bộ nhớ (s), bạn không xử lý đối tượng của bạn, hoặc bạn cần phần cứng tốt hơn :)

+0

IMHO, trong trường hợp này bắt ngoại lệ bằng trình gỡ lỗi sẽ vô dụng, thiệt hại (rò rỉ bộ nhớ nhiều nhất có thể) đã được thực hiện ở một nơi khác. – Naveen

+0

Chỉ cần ném ra một vài lựa chọn. Không thể đau để nhìn. – tsilb

+0

tslib có một điểm, đôi khi bạn có thể thu hẹp khi bạn hết bộ nhớ. – Gregory

9

Có một vài lựa chọn. Các bộ nhớ chuyên dụng như ANTS Memory Profiler từ RedGate có thể rất hữu ích cho việc khắc phục sự cố loại vấn đề này.

Nếu bạn không muốn chi tiền cho một công cụ chuyên dụng, bạn cũng có thể sử dụng WinDbg (một phần của Debugging tools for Windows, một bản tải xuống miễn phí từ Microsoft). Nó có thể cho bạn thấy cách sử dụng đống cho vùng heap được quản lý, các vùng AppDomain khác nhau và vv.

Hãy xem this blog để biết các gợi ý về cách sử dụng WinDbg.

Hãy nhớ rằng khắc phục sự cố hết bộ nhớ có thể khó khăn, vì bạn thường không thấy vấn đề thực tế mà chỉ là một triệu chứng. Vì vậy, không giống như một vụ tai nạn nơi ngăn xếp cuộc gọi sẽ cung cấp cho bạn một dấu hiệu khá tốt về nguồn gốc của vấn đề, ngăn xếp cuộc gọi cho một quá trình với OOM có thể tiết lộ rất ít.

Theo kinh nghiệm của tôi, bạn phải xem nơi bộ nhớ được sử dụng. Nó có thể được trên heap được quản lý, trong trường hợp đó bạn phải tìm hiểu xem có cái gì đó đang nắm giữ trên trường hợp lâu hơn cần thiết. Tuy nhiên, nó cũng có thể liên quan đến việc tải rất nhiều assembly (thường là các assembly được tạo ra khi đang bay).

+0

+1 trên trình thu thập bộ nhớ ANTS! – tijmenvdk

+0

+1 Chỉ cần đăng nội dung nào đó hữu ích. – Gregory

+0

ANTS, công cụ tuyệt vời. – BennyM

3

Hãy xem bài viết này MSDN về cách phát hiện rò rỉ bộ nhớ trong các ứng dụng .NET.

Có lẽ bạn có một số vấn đề trong đó bộ nhớ được phân bổ và không bao giờ được thu thập.

1

PeakWorkingSet của bạn cho biết số chung khi 32bit CLR bắt đầu ném bom.

Mặc dù những gì mọi người nói với bạn, và mặc dù rất trớ trêu về quản lý bộ nhớ tự động, bạn phải biết điều này và đảm bảo rằng bạn không bao giờ đạt đến giới hạn đó trên hệ thống 32bit. Nhiều người không biết về nó và tôi thường thích chọn lên xuống C# bloat downvotes của họ, nhưng khi bạn chạy một vài ứng dụng như vậy trên một máy tính để bàn duy nhất bạn có thể mong đợi một số tàn phá được gây ra.Chỉ cần nhìn vào phần quản lý của VS shutdown, nó giống như một chuyến tàu chạy qua PC.

Có một MemProfiler miễn phí cho .NET, sử dụng nó và tìm kiếm các gốc treo .. cuối cùng, và đặc biệt là khi bạn bắt đầu xử lý dữ liệu kích thước vừa phải, bạn sẽ phải sử dụng thiết kế để phát trực tuyến thay vì dựa vào nó. trên x64 với nhiều RAM hơn.

Và có bộ dữ liệu c880MB có kích thước thảm hại trong những ngày này .. FACT!

[Piece to C# 3.0 sheep]

10

Chúng tôi cung cấp công cụ cho điều đó.

http://msdn.microsoft.com/en-us/library/ms979205.aspx

CLR Profiler cho phép bạn nhìn vào đống quản lý của một quá trình và điều tra hành vi của các nhà sưu tập rác. Sử dụng các chế độ xem khác nhau trong công cụ, bạn có thể có được thông tin hữu ích về việc thực hiện, phân bổ và bộ nhớ thực hiện, phân bổ và bộ nhớ tiêu thụ ứng dụng của bạn.

Sử dụng CLR Profiler, bạn có thể xác định mã mà phân bổ quá nhiều bộ nhớ , gây ra quá nhiều rác bộ sưu tập, và giữ vào bộ nhớ quá lâu.

0

Có lẽ bạn nên kiểm tra các địa điểm nơi bạn sử dụng tài nguyên không được quản lý, trước tiên. Vấn đề có thể là bạn không giải phóng chúng, hoặc bạn không làm điều đó một cách chính xác.

2

Tôi có chính xác cùng một ứng dụng. :) ứng dụng của chúng tôi sử dụng để mất đến 10GB RAM. Điều này rõ ràng là xấu. Sau khi tối ưu hóa một số, tôi đã giảm được mức sử dụng bộ nhớ khoảng 50 lần, vì vậy bây giờ cùng một tập dữ liệu mất tới 200MB. Ma thuật? Không. :) Tôi đã làm gì:

  1. Một số dữ liệu được lưu trữ trong bộ nhớ nhiều lần (vài bản sao). Tôi đã tạo một bản sao của mỗi nhóm dữ liệu.
  2. Một số dữ liệu được lưu trữ dưới dạng string, nhưng cách hiệu quả hơn là int vì các chuỗi đó chỉ chứa chữ số.
  3. Lớp lưu trữ dữ liệu chính là . We wrote our own dictionary không lưu trữ bất kỳ băm nào - do việc sử dụng bộ nhớ kết quả giảm 3 lần trên các hệ thống 64 bit và 2 lần trên các hệ thống 32 bit.

Vì vậy, câu hỏi của tôi là: lớp/đối tượng chính bạn sử dụng để lưu trữ dữ liệu là gì? Loại dữ liệu bạn lưu trữ?

0

Rất nhiều giải pháp hữu ích đã được đề xuất và bài viết MSDN rất kỹ lưỡng. Cùng với những gợi ý ở trên, tôi cũng sẽ làm như sau;

Tương quan thời gian của ngoại lệ với tệp nhật ký của bạn để xem điều gì đang diễn ra tại thời điểm ngoại lệ OOM. Nếu bạn có ít đăng nhập vào thông tin hoặc mức độ gỡ lỗi tôi sẽ đề nghị thêm một số đăng nhập để bạn có một ý tưởng về bối cảnh xung quanh lỗi này.

Việc sử dụng bộ nhớ có tăng dần trong một khoảng thời gian dài trước khi ngoại lệ (ví dụ: quy trình máy chủ chạy vô thời hạn) hay không tăng lên nhanh chóng tăng nhanh cho đến khi ngoại lệ?Có rất nhiều chủ đề đang chạy hoặc chỉ một chủ đề?

Nếu điều đầu tiên là đúng và ngoại lệ không xảy ra trong một thời gian dài, điều đó có nghĩa là tài nguyên bị rò rỉ bị rò rỉ như đã nêu ở trên. Nếu sau này đúng là một số thứ có thể đóng góp vào nguyên nhân, ví dụ: một vòng lặp phân bổ rất nhiều bộ nhớ cho mỗi lần lặp lại, nhận được rất nhiều kết quả từ một dịch vụ, v.v.

Hoặc là tệp nhật ký sẽ cung cấp cho bạn đủ thông tin về nơi bắt đầu. Từ đó tôi sẽ đảm bảo rằng tôi có thể tạo lại lỗi bằng cách phát hành một bộ lệnh nhất định trong giao diện hoặc bằng cách sử dụng bộ đầu vào nhất quán. Sau đó tùy thuộc vào trạng thái của mã tôi sẽ thử (với việc sử dụng thông tin tệp nhật ký) để tạo một số kiểm tra tích hợp nhắm mục tiêu nguồn giả định của sự cố. Điều này sẽ cho phép bạn tạo lại điều kiện lỗi nhanh hơn nhiều và làm cho việc tìm kiếm dễ dàng hơn rất nhiều vì mã bạn đang tập trung vào sẽ nhỏ hơn rất nhiều.

Những thứ khác mà tôi có xu hướng làm là mã nhạy cảm với bộ nhớ đệm với một lớp lược tả nhỏ. Điều này có thể ghi lại việc sử dụng bộ nhớ vào tệp nhật ký và cung cấp cho bạn khả năng hiển thị ngay lập tức các sự cố trong nhật ký. Lớp này có thể được tối ưu hóa để nó không được biên dịch thành bản phát hành hoặc có chi phí hoạt động cực nhỏ (nếu bạn cần thêm thông tin, hãy liên hệ với tôi). Cách tiếp cận này không hoạt động tốt khi nhiều luồng được phân bổ

Bạn đã đề cập đến tài nguyên không được quản lý Tôi giả sử tất cả mã bạn/nhóm của bạn đã viết được quản lý? Nếu không và nếu có thể, tôi sẽ bao quanh các ranh giới không được quản lý với một lớp lược tả tương tự như lớp được đề cập ở trên để loại trừ rò rỉ từ mã không được quản lý hoặc interop. Ghim nhiều con trỏ không được quản lý cũng có thể gây ra phân đoạn heap nhưng nếu bạn không có mã không được quản lý, cả hai điểm này đều có thể bỏ qua.

Gọi rõ ràng bộ thu gom rác trong một nhận xét trước đó đã không được khuyến khích. Mặc dù bạn hiếm khi làm điều này vào những thời điểm có hiệu lực (tìm kiếm blog của Rico Mariani để biết ví dụ). Một ví dụ (được đề cập trong blog được đề cập) trong đó tôi đã gọi thu thập rõ ràng là khi một lượng lớn chuỗi đã được trả về từ một dịch vụ, được đưa vào tập dữ liệu và sau đó được liên kết với lưới. Ngay cả sau khi màn hình đã bị đóng, bộ nhớ này không được thu thập trong một thời gian. Nói chung nó không nên được gọi một cách rõ ràng như các bộ thu gom rác duy trì các số liệu mà nó dựa trên (trong số những thứ khác) bộ sưu tập trên. Việc thu thập cuộc gọi thu thập một cách rõ ràng sẽ làm vô hiệu các chỉ số này.

Cuối cùng, bạn nên có ý tưởng về các yêu cầu về bộ nhớ trong ứng dụng của mình. Hoặc có được điều này bằng cách đăng nhập thông tin nhiều hơn, đôi khi chạy các trình kiểm tra profiler, stress/unit/integration. Nhận ý tưởng về tác động của một hoạt động nhất định ở cấp cao, ví dụ: dựa trên một tập hợp các đầu vào khoảng x sẽ được phân bổ. Tôi hiểu được điều này bằng cách đăng xuất thông tin chi tiết tại các điểm chiến lược trong tệp nhật ký. Tệp nhật ký cồng kềnh có thể khó hiểu hoặc giải thích.

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