2013-03-28 19 views
5

Tôi đã chạy một số thử nghiệm, để xem cách đăng nhập của tôi sẽ thực hiện thay vì làm File.AppendAllText trước tiên tôi sẽ ghi vào luồng bộ nhớ và sau đó sao chép vào tệp. Vì vậy, chỉ để xem làm thế nào nhanh chóng hoạt động bộ nhớ là tôi đã làm điều này ..tại sao tôi nhận được System.OutOfMemoryException ngay cả khi có khoảng 700Mb RAM miễn phí?

private void button1_Click(object sender, EventArgs e) 
    { 
     using (var memFile = new System.IO.MemoryStream()) 
     { 
      using (var bw = new System.IO.BinaryWriter(memFile)) 
      { 
       for (int i = 0; i < Int32.MaxValue; i++) 
       { 
        bw.Write(i.ToString() + Environment.NewLine); 
       } 
       bw.Flush(); 
      } 
      memFile.CopyTo(new System.IO.FileStream(System.IO.Path.Combine("C", "memWriteWithBinaryTest.log"), System.IO.FileMode.OpenOrCreate)); 
     } 
    } 

Khi i đạt 25413324 tôi nhận được một Exception of type 'System.OutOfMemoryException' was thrown. mặc dù tôi Process Explorer nói rằng tôi có khoảng 700MB ram miễn phí ???

Sau đây là các ảnh chụp màn hình (chỉ trong trường hợp)

Process Explorer enter image description here

Đây là Winform

enter image description here

EDIT: Vì lợi ích của các đối tượng hơn được tạo ra trên heap, tôi đã viết lại số bw.write vào số

này
bw.Write(i); 
+0

Theo câu trả lời, 'BinaryWriter' không viết ra biểu diễn chuỗi của số. Nó sẽ viết biểu diễn byte. Có lẽ bạn muốn thứ hai, nhưng từ mã gốc nó không xuất hiện như vậy. – leppie

Trả lời

9

Trước hết, bạn hết bộ nhớ vì bạn tích lũy dữ liệu trong MemoryStream, thay vì viết trực tiếp vào FileStream. Sử dụng trực tiếp FileStream và bạn sẽ không cần nhiều RAM (nhưng bạn sẽ phải giữ cho tệp mở).

Số lượng bộ nhớ vật lý không được sử dụng không liên quan trực tiếp đến ngoại lệ này, điều này có vẻ lạ.

Điều đáng quan tâm là:

  • rằng bạn có một đoạn tiếp giáp của bộ nhớ có sẵn trong quá trình ảo không gian địa chỉ
  • rằng hệ thống cam kết không vượt quá tổng kích thước RAM + page file kích thước

Khi bạn yêu cầu trình quản lý bộ nhớ Windows phân bổ cho bạn một số RAM, bạn cần kiểm tra xem số lượng là có sẵn, nhưng số tiền đó có bao nhiêu hứa hẹn để cung cấp cho mọi quy trình khác. Việc hứa hẹn đó được thực hiện thông qua các cam kết.Để cam kết một số bộ nhớ có nghĩa là trình quản lý bộ nhớ cung cấp cho bạn bảo đảm rằng sẽ khả dụng khi bạn sử dụng nó.

Vì vậy, có thể là RAM vật lý được sử dụng hết, nhưng yêu cầu phân bổ của bạn vẫn thành công. Tại sao? Vì có rất nhiều không gian có sẵn trong tệp trang. Khi bạn thực sự bắt đầu sử dụng RAM mà bạn có được thông qua phân bổ như vậy, người quản lý bộ nhớ sẽ chỉ đơn giản là trang ra một cái gì đó khác. Vì vậy, 0 vật lý RAM! = Phân bổ sẽ thất bại.

Điều ngược lại cũng có thể xảy ra; một phân bổ có thể thất bại mặc dù có một số RAM vật lý không sử dụng. Quá trình của bạn nhìn thấy bộ nhớ thông qua không gian địa chỉ ảo được gọi là. Khi quá trình của bạn đọc bộ nhớ tại địa chỉ 0x12340000, đó là địa chỉ ảo. Nó có thể ánh xạ tới RAM tại 0x78650000 hoặc tại 0x000000AB12340000 (chạy một quy trình 32 bit trên hệ điều hành 64 bit), nó có thể trỏ đến thứ gì đó chỉ tồn tại trong tệp trang hoặc thậm chí không thể trỏ vào bất kỳ thứ gì.

Khi bạn muốn cấp phát một khối bộ nhớ với các địa chỉ tiếp giáp, nó nằm trong không gian địa chỉ ảo mà RAM cần phải tiếp giáp. Đối với quy trình 32 bit, bạn chỉ nhận được 2GB hoặc 3 GB dung lượng địa chỉ có thể sử dụng, vì vậy không quá khó để sử dụng nó theo cách không có kích thước đủ lớn, mặc dù có cả RAM vật lý miễn phí và đủ tổng không gian địa chỉ ảo chưa sử dụng.

3

Điều này có thể do phân mảnh bộ nhớ gây ra.

Đối tượng lớn đi vào đống vật thể lớn và chúng không được di chuyển xung quanh để nhường chỗ cho mọi thứ. Điều này có thể gây ra phân mảnh khi bạn có khoảng trống trong bộ nhớ có sẵn, điều này có thể gây ra bộ nhớ ngoài khi bạn cố gắng phân bổ một đối tượng lớn hơn bất kỳ khối bộ nhớ có sẵn nào.

See here for more details.

Bất kỳ đối tượng nào lớn hơn 85.000 byte sẽ được đặt trên heap đối tượng lớn, ngoại trừ mảng tăng gấp đôi mà ngưỡng chỉ là 1000 đôi (hoặc 8000 byte). Cũng cần lưu ý rằng các chương trình 32 bit .Net được giới hạn tối đa 2GB cho mỗi đối tượng và ít hơn 4GB tổng thể (có thể thấp đến 3GB tùy theo hệ điều hành).

0

Bạn không được sử dụng BinaryWriter để viết văn bản vào tệp. Thay vào đó, hãy sử dụng TextWriter.

Bây giờ bạn đang sử dụng:

for (int i = 0; i < Int32.MaxValue; i++)

này sẽ viết ít nhất 3 byte cho mỗi write (đại diện số lượng và newline). Thời gian của Int32.MaxValue và bạn cần ít nhất 6 GB bộ nhớ đã thấy rằng bạn đang viết nó vào một MemoryStream.

Nhìn xa hơn vào mã của bạn, bạn sẽ viết MemoryStream vào một tệp theo bất kỳ cách nào. Vì vậy, bạn chỉ có thể làm như sau:

for (int i = 0; i < int.MaxValue; i++) 
{ 
    File.AppendAllText("filename.log", i.ToString() + Environment.Newline); 
} 

hoặc gửi thư cho một mở TextWriter:

TextWriter writer = File.AppendText("filename.log"); 

for (int i = 0; i < int.MaxValue; i++) 
{ 
    writer.WriteLine(i); 
} 

Nếu bạn muốn có một số bộ nhớ đệm, mà IMO là một ý tưởng tồi cho khai thác gỗ là bạn sẽ mất người cuối cùng bit của viết trong một vụ tai nạn, bạn có thể sử dụng sau khi tạo TextWriter:

StreamWriter(string path, bool append, Encoding encoding, int bufferSize) 

và vượt qua một số 'hơi to' cho bufferSize. Giá trị mặc định là 1024.

Để trả lời câu hỏi, bạn có được một ngoại lệ bộ nhớ do việc thay đổi kích thước MemoryStream và tại một thời điểm nào đó nó trở nên lớn để phù hợp với bộ nhớ (được thảo luận trong câu trả lời khác).

+0

Vâng không chính xác một câu hỏi để đăng nhập, tôi biết về Logger.Net vv, nhưng trường hợp này chỉ là một bản ghi đơn giản, tôi cũng cần biết hiệu suất sẽ được cải thiện bao nhiêu (nghĩa là thời gian sẽ tốn bao nhiêu) ghi vào bộ nhớ và sau đó để tập tin. – Razort4x