我正在运行一些测试,看看我的日志记录将如何执行而不是File.AppendAllText
我首先写入内存流然后复制到文件。所以,只是为了看看内存操作有多快,我就这样做了。
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));
}
}
当i
到达25413324
时我得到Exception of type 'System.OutOfMemoryException' was thrown.
即使我的Process Explorer说我有大约700Mb的免费公羊???
以下是截屏(以防万一)
Process Explorer
这是winform
编辑:为了在堆上创建更多对象,我将bw.write
重写为此
bw.Write(i);
答案 0 :(得分:9)
首先,由于您在MemoryStream
中累积数据而不是直接将其写入FileStream
,因此内存不足。直接使用FileStream
,您根本不需要太多RAM(但您必须保持文件打开)。
未使用的物理内存量与此异常没有直接关系,可能听起来很奇怪。
重要的是:
当你要求Windows内存管理器为你分配一些内存时,它需要检查可用多少,但是承诺可以提供多少内存每一个过程。这样的承诺是通过提交来完成的。要提交某些内存意味着内存管理器为您提供了保证,当您最终使用它时将可用。
因此,可能是物理RAM已经完全耗尽,但您的分配请求仍然成功。为什么?因为页面文件中有很多可用空间。当你真正开始使用RAM时,通过这样的分配,内存管理器只会简单地分页。所以0物理RAM!=分配将失败。
反之亦然;尽管有一些未使用的物理RAM,分配仍会失败。您的进程通过所谓的虚拟地址空间查看内存。当您的进程在地址0x12340000
读取内存时,这是一个虚拟地址。它可能映射到0x78650000
或0x000000AB12340000
的RAM(在64位操作系统上运行32位进程),它可能指向仅存在于页面文件中的内容,或者它可能甚至根本没有任何意义。
如果要分配具有连续地址的内存块,则在此虚拟地址空间中RAM需要是连续的。对于32位进程,您只能获得2GB或3GB的可用地址空间,因此尽管存在可用的物理RAM和足够的空间,但是使用它并不太难以存在足够大小的连续块。总未使用的虚拟地址空间。
答案 1 :(得分:3)
这可能是由内存碎片造成的。
大对象进入大对象堆并且它们不会被移动以为事物腾出空间。如果您在可用内存中存在间隙,这可能会导致碎片,当您尝试分配大于任何可用内存块的对象时,这可能会导致内存不足。
任何大于85,000字节的对象都将被放置在大对象堆上,除了双精度数组,其阈值只有1000倍(或8000字节)。
另请注意,32位.Net程序限制为每个对象最多2GB,总体上略低于4GB(可能低至3GB,具体取决于操作系统)。
答案 2 :(得分:0)
您不应该使用BinaryWriter
将文本写入文件。请改用TextWriter
。
现在您正在使用:
for (int i = 0; i < Int32.MaxValue; i++)
这将写入每个写入至少3个字节(数字表示和换行符)。时间是Int32.MaxValue
并且您需要至少6GB的内存已经看到您正在将其写入MemoryStream
。
进一步查看代码,您将以任何方式将MemoryStream
写入文件。所以你可以简单地做到以下几点:
for (int i = 0; i < int.MaxValue; i++)
{
File.AppendAllText("filename.log", i.ToString() + Environment.Newline);
}
或写一个开放的TextWriter
:
TextWriter writer = File.AppendText("filename.log");
for (int i = 0; i < int.MaxValue; i++)
{
writer.WriteLine(i);
}
如果你想要一些内存缓冲区,哪个IMO对于日志记录是一个坏主意,因为你会在崩溃期间丢失最后一点写入,你可以使用以下内容创建TextWriter
:
StreamWriter(string path, bool append, Encoding encoding, int bufferSize)
并传递bufferSize
的'biggish'号码。默认值为1024
。
要回答这个问题,由于MemoryStream
调整大小会导致内存不足,并且在某些时候它会变大以适应内存(在另一个答案中已经讨论过)。