2013-06-11 17 views
10

Tôi đã viết đoạn code sau để kiểm tra xem có đủ bộ nhớ,MemoryFailPoint luôn ném một InsufficientMemoryException ngay cả khi bộ nhớ có sẵn

while (true) 
{ 
    try 
    { 
     // Check for available memory. 
     memFailPoint = new MemoryFailPoint(250); 

     break; 
    } 
    catch (InsufficientMemoryException ex) 
    { 
     if (memFailPoint != null) 
     { 
      memFailPoint.Dispose(); 
     } 

     Thread.Sleep(waitSecond * 1000); 
    } 
} 

Tôi đang chạy ở trên trong một ứng dụng giao diện điều khiển trên 7 máy tính Windows 64-bit.

Có 4 cuộc gọi cứ 10 giây một lần cho phương pháp này.

Ban đầu nó hoạt động tốt, nhưng sau 2-3 giờ, luôn luôn có một số InsufficientMemoryException được ném. Tôi đã kiểm tra bộ nhớ khả dụng và nó hiển thị hơn 1 GB.

Tôi đã thử rất nhiều nhưng tôi không thể tìm thấy lý do tại sao điều này xảy ra.

Sau đây là stack trace:

at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes) 
at SocketListner.AcceptConnection(IAsyncResult res) in H:\Projects\SocketListner.cs:line 308 

Không có ngoại lệ bên trong.

+0

Bạn đang làm gì khác để nhận lỗi này? Có theo dõi ngăn xếp bạn có thể đăng không? –

+0

Tôi đã thêm dấu vết ngăn xếp, vui lòng truy cập lại câu hỏi –

+4

"Tôi đã kiểm tra bộ nhớ khả dụng" như thế nào? Ngay cả với 1GB miễn phí, nó có thể quá phân mảnh để thực hiện phân bổ 250Meg. Sẽ cần đến 250M không gian liền kề miễn phí trong bộ nhớ để thành công. – spender

Trả lời

16

Bạn có thể dựa vào phương pháp này làm việc một cách chính xác, ngoại lệ này là rất khả năng cho chuyến đi trong quy trình 32 bit khi bạn yêu cầu 250 megabyte. Điều đó trở nên khó khăn khi chương trình đã chạy trong một thời gian.

Chương trình không bao giờ gặp sự cố với OOM vì bạn đã tiêu thụ hết dung lượng bộ nhớ ảo sẵn có. Nó bị treo vì không còn một lỗ nào trong không gian địa chỉ đủ lớn để phù hợp với việc phân bổ. Mã của bạn yêu cầu một lỗ đủ lớn để phân bổ 250 megabyte trong một gulp. Khi bạn không nhận được ngoại lệ, bạn có thể chắc chắn rằng phân bổ này sẽ không thành công.

Nhưng 250 megabyte khá nhiều, đó là một mảng thực sự lớn. Và rất có khả năng thất bại do một vấn đề được gọi là "phân mảnh không gian địa chỉ". Nói cách khác, một chương trình thường bắt đầu với một số lỗ rất lớn, lớn nhất khoảng 600 megabyte. Các lỗ có sẵn giữa các phân bổ được thực hiện để lưu trữ mã và dữ liệu được sử dụng bởi thời gian chạy .NET và các tệp DLL Windows không được quản lý. Khi chương trình phân bổ nhiều bộ nhớ hơn, các lỗ đó sẽ nhỏ hơn. Nó có khả năng phát hành một số bộ nhớ nhưng điều đó không tái tạo một lỗ lớn. Bạn thường nhận được hai lỗ, bằng một nửa kích thước của bản gốc, với phân bổ ở đâu đó ở giữa cắt lỗ lớn ban đầu thành hai.

Điều này được gọi là phân mảnh, quy trình 32 bit phân bổ và giải phóng nhiều bộ nhớ sẽ phân mảnh không gian địa chỉ bộ nhớ ảo sao cho lỗ lớn nhất vẫn có sẵn sau một thời gian nhỏ hơn, khoảng 90 megabyte là khá điển hình.Yêu cầu 250 megabyte gần như được bảo đảm là không thành công. Bạn sẽ cần phải nhắm thấp hơn.

Bạn không nghi ngờ rằng nó hoạt động khác nhau, đảm bảo rằng số tiền tổng số phân bổ thêm tối đa 250 megabyte được đảm bảo hoạt động. Tuy nhiên, đây không phải là cách thức hoạt động của MemoryFailPoint, nó chỉ kiểm tra việc phân bổ có thể là lớn nhất. Không cần phải nói có lẽ, điều này làm cho nó ít hơn hữu ích. Nói cách khác, tôi thông cảm với các lập trình viên .NET framework, làm cho nó hoạt động theo cách chúng ta muốn nó vừa tốn kém vừa không thể cung cấp một sự đảm bảo vì kích thước của một phân bổ quan trọng nhất.

Bộ nhớ ảo là một nguồn tài nguyên dồi dào vô cùng rẻ. Nhưng nhận được gần tiêu thụ tất cả là rất phiền hà. Một khi bạn tiêu thụ một gigabyte của nó sau đó OOM nổi bật ngẫu nhiên là bắt đầu có khả năng. Đừng quên sửa chữa dễ dàng cho vấn đề này, bạn đang chạy trên một hệ điều hành 64-bit. Vì vậy, việc thay đổi mục tiêu nền tảng EXE thành AnyCPU sẽ giúp bạn gobs và gobs của không gian địa chỉ ảo. Phụ thuộc vào phiên bản hệ điều hành nhưng có thể có terabyte. Nó vẫn còn mảnh vỡ nhưng bạn không quan tâm nữa, các lỗ hổng rất lớn.

Cuối cùng nhưng không kém phần quan trọng, hiển thị trong nhận xét, sự cố này có không có gì để làm với RAM. Bộ nhớ ảo khá không liên quan đến lượng RAM bạn có. Đây là công việc của hệ điều hành để ánh xạ các địa chỉ bộ nhớ ảo đến các địa chỉ vật lý trong RAM, nó hoạt động như vậy. Việc truy cập vị trí bộ nhớ có thể làm hỏng một lỗi trang, hệ điều hành sẽ cấp phát RAM cho trang. Và ngược lại xảy ra, hệ điều hành sẽ unmap RAM cho một trang khi nó là cần thiết ở nơi khác. Bạn không bao giờ có thể hết RAM, máy sẽ chậm lại để thu thập dữ liệu trước khi có thể xảy ra. Tiện ích VMMap của SysInternals rất đẹp để xem không gian địa chỉ ảo của chương trình của bạn trông như thế nào, mặc dù bạn có xu hướng bị chết đuối trong thông tin cho một quy trình lớn.

+0

Cảm ơn bạn đã phân tích chi tiết. Tôi đã giảm kích thước xuống 50MB và đặt các ứng dụng trong thử nghiệm, tuy nhiên tôi đã xây dựng trong 64bit nên OOM ngoại lệ không nên xảy ra, vì nó đã bị bắt trước đó. –

+0

Tôi phải nói, chỉ cần tự ý cắt nó từ 250 đến 50 là khá ngẫu nhiên.Điều quan trọng là số bạn chọn có một số sự thật và đại diện cho số lượng bộ nhớ bạn sẽ cần. –

+0

làm thế nào để biết điều đó ?, một khi một gói được xử lý bộ nhớ nên được miễn phí, nhưng sau một thời gian dài Cơ sở dữ liệu trở thành nút cổ chai và dữ liệu ở lại trong bộ nhớ tối đa thời gian chờ, tức là 30 giây. –

0

Xem xét sử dụng các phương pháp GC.GetTotalMemory để xác định dung lượng bộ nhớ có sẵn trước và sau khi gọi:

memFailPoint = new MemoryFailPoint(250); 

InsufficientMemoryException được ném trước khi bắt đầu một hoạt động, bởi các nhà xây dựng MemoryFailPoint khi bạn chỉ định một cấp phát bộ nhớ dự kiến ​​lớn hơn lượng bộ nhớ hiện có. Giống như user7116 commented, đó là lý do tại sao bạn nên kiểm tra trước.

Ví dụ trong liên kết này sẽ cung cấp cho bạn một giải pháp: MemoryFailPoint Class

Bạn cũng có thể kiểm tra MSDN blog của bài viết này: Out of memory? Easy ways to increase the memory available to your program

+0

Cảm ơn jszigeti :) – terrybozzio

0

kiểm tra MemoryFailPoint cho bộ nhớ có sẵn liên tiếp như tài liệu ở đây: http://msdn.microsoft.com/fr-fr/library/system.runtime.memoryfailpoint.aspx

Bạn có thể tiêu thụ rất ít bộ nhớ, nhưng đã bị phân mảnh nó rất nhiều và sau đó là tại không thể phân bổ một khối liên tiếp của ký ức về kích thước cần thiết. Nó rất điển hình của vấn đề này xảy ra sau một vài giờ. Để tránh điều đó, hãy sử dụng một nhóm đối tượng cho đối tượng mà bạn tiếp tục instantiating, nó sẽ làm cho không gian bộ nhớ sử dụng cứng nhắc hơn.

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