FileStream.Write()抛出OutOfMemoryException的可能原因?

我有10个线程将数千个小缓冲区(每个16-30个字节)写入随机位置的大文件中。 一些线程在FileStream.Write()opreation上抛出OutOfMemoryException。

导致OutOfMemoryException的原因是什么? 要找什么?

我正在使用像这样的FileStream(对于每个书面项目 – 此代码从10个不同的线程运行):

using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, BigBufferSizeInBytes, FileOptions.SequentialScan)) { ... fs.Write(); } 

我怀疑在FileStream中分配的所有缓冲区都不会被GC及时释放。 我不明白为什么CLR而不是抛出不只是运行GC循环并释放所有未使用的缓冲区?

如果您的代码显示十个线程正在打开文件,那么您一次最多可以有十个未处置的FileStream对象。 是的,FileStream 确实有一个内部缓冲区,您在代码中使用“BigBufferSizeInBytes”指定的大小。 你能否透露确切的价值? 如果这足够大(例如~100MB)那么它很可能是问题的根源。

默认情况下(即在构造时未指定数字时),此缓冲区为4kB,对于大多数应用程序而言通常都可以。 一般来说,如果你真的关心磁盘写入性能,那么你可以将这个增加到几个100kB而不是更多。

但是,对于您的特定应用程序,这样做没有多大意义,因为在Dispose()FileStream对象之前,所述缓冲区将永远不会包含超过您写入的16-30个字节。

要回答您的问题,只有在GC运行无法分配请求的内存时,才会抛出OutOfMemoryException。 同样,如果缓冲区非常大,那么系统可能会留下足够的内存,而不是连续的块。 这是因为大对象堆永远不会被压缩。

我已经几次提醒人们这个,但是大对象堆可以相当主要地抛出exception,当看似你有足够的可用内存或者应用程序运行正常时。

我几乎完全按照你在这里描述的内容做了相当频繁的问题。

您需要发布更多代码才能正确回答此问题。 但是,我猜它也可能与潜在的万圣节问题有关(Spooky Dooky) 。

您正在读取的缓冲区也可能是问题(同样是大对象堆相关),您还需要提供有关循环中正在发生的事情的更多详细信息。 我刚刚找到了我遇到的最后一个几乎相同的错误(我正在执行许多并行哈希更新,它们都需要在输入文件的读取中保持独立状态)….

OOP! 刚滚动并注意到“BigBufferSizeInBytes”,我再次倾向于大物体堆……

如果我是你,(由于缺乏上下文,这是非常困难的),我会提供一个小的调度“mbuf”,你复制进出,而不是让你所有的独立线程分别阅读你的大背景array …(即很难不使用非常恶劣的代码语法导致不完整的分配)。

缓冲区通常不在FileStream中分配。 也许问题是“编写成千上万的小缓冲区”这一行 – 你真的是这个意思吗? 通常,您会多次,多次(即对不同的读/写调用)重复使用缓冲区。

另外 – 这是一个单独的文件吗? 单个FileStream不保证是线程安全的……所以如果你没有进行同步,那么就会出现混乱。

这些限制可能源于底层操作系统,并且.NET Framework无法克服这些限制。

我无法从您的代码示例中推断出,您是否同时打开了很多这些FileStream对象,或者是否按顺序打开它们。 使用’using’关键字将确保在fs.Write()调用后关闭文件。 关闭文件不需要GC循环。

FileStream类实际上适用于对文件的顺序读/写访问。 如果您需要快速写入大文件中的随机位置,您可能需要查看使用虚拟文件映射。

更新:似乎虚拟文件映射在.NET中不会得到官方支持,直到4.0。 您可能希望了解此function的第三方实现。

戴夫

我正在经历类似的事情,并想知道你是否曾经固定问题的根源?

我的代码在文件之间进行了大量的复制,在不同的字节文件之间传递了相当多的megs。 我注意到,虽然进程内存使用率保持在一个合理的范围内,但系统内存分配在复制过程中会过高 – 比我的进程使用的内存分配要多得多。

我已经将问题跟踪到FileStream.Write()调用 – 当这行被取出时,内存使用似乎按预期进行。 我的BigBufferSizeInBytes是默认值(4k),我无法看到这些可以收集到的任何地方……

感谢您在查看问题时发现的任何内容!