【问题标题】:FileStream and memory usageFileStream 和内存使用情况
【发布时间】:2010-10-21 20:12:28
【问题描述】:

我编写了以下程序,其目的是创建一个给定大小的文件,其中包含一些随机数据。该程序运行良好,并且执行了它应该做的事情。但是,我不明白为什么它会消耗 5GB 的 RAM(请参阅我的任务管理器的屏幕截图)。当我用随机数据编写文件时,我没有创建新对象。我错过了什么?我希望这个程序完全不占用内存。

我现在最大的问题是,在文件生成的中间,机器正在死机......

class Program
{
    static void Main(string[] args)
    {
        CreateFile("test.dat", 10 * 1024 * 1024);
    }

    public static void CreateFile(string path, long approximativeFileSizeInKb)
    {
        RandomNumberGenerator randomNumber = RandomNumberGenerator.Create();

        byte[] randomData = new byte[64 * 1024];

        int numberOfIteration = 0;
        randomNumber.GetNonZeroBytes(randomData);

        using (FileStream fs = File.Create(path, 64 * 1024))
        {
            while (numberOfIteration++ * 64 < approximativeFileSizeInKb)
            {
                fs.Write(randomData, 0, randomData.Length);
            }
        }
    }
}

【问题讨论】:

  • 我刚试过你的程序,一开始我看到增加了~1GB。然后内存使用一直保持到最后。
  • Gonzalo - 很高兴听到这个消息...但是仍然有很多 RAM 要写入磁盘?你是 x86 还是 x64?
  • 我现在最大的问题是在文件生成的中间,机器正在死机......
  • 您能否向我们展示此进程的所有内存相关列,如“进程”选项卡中所示?工作集、页面文件使用等...
  • 我不知道内存问题,但我对随机数据很好奇。使用 RandomNumberGenerator 是否有特殊原因,它可能比 System.Random 更慢并且开销更大?此外,您再次重复相同的随机 64 KiB 块并省略空字节,这与加密安全的随机数生成器结合使用似乎有点奇怪。

标签: .net file-io .net-4.0 memory-management filestream


【解决方案1】:

更改您的行:

using (FileStream fs = File.Create(path, 64 * 1024))

using (FileStream fs = File.Create(path, 64 * 1024, FileOptions.WriteThrough))

看看这对你有什么影响。

【讨论】:

  • 太棒了!有了那个标志,RAM 保持超级平坦...... :D 我很高兴。想起来好像有点慢..
  • 这是速度与空间的经典权衡。您可以通过将第二个参数调整为超过 64Kb 来重新获得一些速度。尽管尝试将其保持在 4Kb 边界以匹配 Windows 默认内存页面大小。
  • 哇,这么多内存,故意不使用它。这是犯罪。 -1,而且我不会经常在回答时说出来。
  • 真的吗?为了直接解决他的确切需求,它被否决了?我没有看到对给出相同建议的其他答案投反对票。
  • +1 取消上述 -1 以准确解决所要求的问题!
【解决方案2】:

文件系统写入始终由操作系统缓冲。

您调用 FileSystem.Write 的速度比您的硬件处理写入的速度快,因此操作系统正在缓存您的所有写入。

即使您调用 FileSystem.Flush,您的写入速度仍然会比您的硬件处理写入的速度快。

获得更快的硬盘子系统。最好使用具有大量板载内存的 RAID 控制器,该控制器连接到大型 RAID 5 或 6 阵列,其中基于服务器的硬盘驱动器具有 64MB 缓存设置和写入缓冲。

(为了缓解这种行为,请将标志 FileOptions.WriteThrough 添加到您的 File.Create 调用中。)

【讨论】:

    【解决方案3】:

    Windows 似乎在使用文件系统缓存...不是应用程序

    【讨论】:

      【解决方案4】:

      我从各个站点拼凑起来......问题是即使使用直写选项,Windows 仍然会缓存所有内容。因此,如果您使用标准 FileStream,您的“可用”内存会下降,如果尝试对超大文件执行某些操作并同时捕获高速率数据,这可能会出现问题。顺便说一句,当您关闭应用程序时,Windows 仍会缓存文件。这对于您想要编写并忘记的文件很有用...... .Net 似乎认为您不需要。

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.IO;
      using System.Runtime.InteropServices;
      using Microsoft.Win32.SafeHandles;
      
      namespace ConsoleApplication2
      {
          class Program
          {
              [DllImport("KERNEL32", SetLastError = true)]
              public extern static int CloseHandle(IntPtr hObject);
              [DllImport("kernel32", SetLastError = true)]
              public static extern unsafe IntPtr CreateFile(
                  string FileName,           // file name 
                  uint DesiredAccess,        // access mode 
                  uint ShareMode,            // share mode 
                  IntPtr SecurityAttributes, // Security Attr 
                  uint CreationDisposition,  // how to create 
                  uint FlagsAndAttributes,   // file attributes 
                  IntPtr hTemplate // template file   
                  );
              static void Main(string[] args)
              {
                  const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
                  const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
                  Random r = new Random(0);
                  IntPtr f = CreateFile(@"e:\test\temp.bin",
                                   (uint)FileAccess.Write,
                                   (uint)FileShare.None,
                                   IntPtr.Zero,
                                   (uint)FileMode.Create,
                                    FILE_FLAG_NO_BUFFERING,
                                   IntPtr.Zero);
                  using (FileStream fs = new FileStream(f,FileAccess.Write,false,1024*1024))
                  {
                      int blocksize = 1024 * 1024;
                      byte[] val = new byte[blocksize];
                      for (int i = 0; i < blocksize; i++)
                      {
                          val[i] = 1;
                      }
                      while (true)
                      {
                          for (int i = 0; i < 1000; i++)
                          {
                              for (int j = 0; j < blocksize; j++)
                              {
                                  fs.WriteByte(val[i]);
                              }
                          }
                          Console.WriteLine("Enter s to stop");
                          ConsoleKeyInfo k = Console.ReadKey();
                          if (k.KeyChar == 's')
                          {
                              break;
                          }
                      }
                  }
                  CloseHandle(f);
                  Console.WriteLine("done");
                  Console.ReadKey();
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        FileStream 维护一个内存缓冲区,允许您的程序尽可能快地输出。您的程序可以将数据填充到缓冲区中,比将缓冲区写入磁盘要快得多,这就是内存跳转的来源。

        不过,实际使用的内存似乎有点偏多;您正在生成一个 10MB 的文件(以 64KB 块为单位)并使用大约 5GB 的内存来执行此操作。除了代码 sn-p 中的内容之外,该程序还有其他内容吗?您是否多次运行它?

        【讨论】:

        • 实际上是10GB(仔细阅读代码),这可以解释机器的缓存有很多未使用的内存。
        【解决方案6】:

        和 Gonzalo 一样,我在我的系统上运行了你的代码,只发现内存使用量增加了 1GB。

        您是否打开了防病毒软件? AV 可能会在写入 .dat 文件时对其进行扫描,从而导致数据在扫描时缓存在内存中,从而导致内存使用量大幅增加。如果您怀疑 AV 是问题的一部分,请尝试将文件扩展名更改为 .dat 以外的其他内容(例如 .txt)。

        要尝试的另一件事是在 fs.Write(...) 之后添加对 fs.Flush() 的调用。

        【讨论】:

        • 我没有 AV。我将文件名更改为 TXT 并添加了 Flush。行为没有变化。 :(
        猜你喜欢
        • 1970-01-01
        • 2012-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多