5

我正在运行一些测试,以查看我的日志记录将如何执行,而不是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到达时,即使我的Process Explorer说我有大约 700Mb 的可用内存,我也25413324得到了一个?Exception of type 'System.OutOfMemoryException' was thrown.

这是屏幕截图(以防万一)

进程浏览器 在此处输入图像描述

这是winform

在此处输入图像描述

编辑:为了在堆上创建更多对象,我重写了bw.write这个

bw.Write(i);
4

3 回答 3

9

首先,你的内存不足是因为你MemoryStreamFileStream. 直接使用FileStream,您根本不需要太多 RAM(但您必须保持文件打开)。

未使用的物理内存量与此异常没有直接关系,这听起来很奇怪。

重要的是:

  • 你在进程的虚拟地址空间中有一块连续的可用内存
  • 系统提交不超过总 RAM 大小 + 页面文件大小

当您要求 Windows 内存管理器为您分配一些 RAM 时,它需要检查的不是有多少可用,而是它承诺为其他所有进程提供多少。这种承诺是通过提交完成的。提交一些内存意味着内存管理器向您提供了保证,当您最终使用它时它将可用。

因此,可能是物理 RAM 已完全用完,但您的分配请求仍然成功。为什么?因为页面文件中有很多可用空间。当您真正开始使用通过这种分配获得的 RAM 时,内存管理器只会简单地分页出其他内容。所以 0 物理 RAM != 分配将失败。

反之亦然;尽管有一些未使用的物理 RAM,但分配可能会失败。您的进程通过所谓的虚拟地址空间查看内存。当您的进程在 address 读取内存时0x12340000,这是一个虚拟地址。它可能会映射到 RAM at0x78650000或 at 0x000000AB12340000(在 64 位操作系统上运行 32 位进程),它可能指向仅存在于页面文件中的东西,或者它甚至可能根本不指向任何东西。

当您要分配具有连续地址的内存块时,RAM 需要在此虚拟地址空间中是连续的。对于 32 位进程,您只能获得 2GB 或 3GB 的可用地址空间,因此以不存在足够大小的连续块的方式使用它并不难,尽管有可用的物理 RAM 和足够的总未使用的虚拟地址空间。

于 2013-03-28T11:04:12.167 回答
3

这可能是由内存碎片引起的。

大对象进入大对象堆,它们不会四处移动以腾出空间。这可能会导致可用内存中存在间隙的碎片,当您尝试分配大于任何可用内存块的对象时,这可能会导致内存不足。

有关详细信息,请参见此处

任何大于 85,000 字节的对象都将被放置在大对象堆中,但阈值仅为 1000 个双精度(或 8000 字节)的双精度数组除外。

另请注意,32 位 .Net 程序被限制为每个对象的最大 2GB,整体略小于 4GB(根据操作系统,可能低至 3GB)。

于 2013-03-28T10:59:08.110 回答
0

您不应该使用 aBinaryWriter将文本写入文件。改用 a 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);
}

或写一个 open 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。默认值为1024.

要回答这个问题,由于MemoryStream调整大小,您会遇到内存不足异常,并且在某些时候它会变得很大以适应内存(在另一个答案中讨论过)。

于 2013-03-28T11:11:49.920 回答