3

我有 10 个线程将数千个小缓冲区(每个 16-30 字节)写入随机位置的大文件。一些线程在 FileStream.Write() 操作上抛出 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 循环并释放所有未使用的缓冲区,而不是抛出?

4

5 回答 5

2

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

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

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

为了回答您的问题,只有在GC 运行无法分配请求的内存时才会引发 OutOfMemoryException 。同样,如果缓冲区真的很大,那么系统可能会剩下大量内存,而不是一个连续的块。这是因为大对象堆永远不会被压缩。

于 2009-02-16T11:30:28.510 回答
1

我已经提醒过人们几次了,但是大对象堆可以相当巧妙地抛出该异常,当您似乎有足够的可用内存或应用程序运行正常时。

当我几乎完全按照您在此处描述的内容时,我经常遇到这个问题。

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

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

哎呀!刚刚滚动并注意到“BigBufferSizeInBytes”,我再次倾向于大对象堆......

如果我是你,(由于缺乏上下文,这非常困难),我会提供一个小的调度“mbuf”,你可以在其中复制进出,而不是让所有分散的线程单独读取你的大背景数组...(即很难不使用非常微妙的代码语法导致不恰当的分配)。

于 2009-05-15T08:26:29.500 回答
0

我正在经历类似的事情,想知道您是否曾经确定过问题的根源?

我的代码在文件之间进行了很多复制,在不同的字节文件之间传递了很多兆。我注意到虽然进程内存使用率保持在合理范围内,但系统内存分配在复制过程中飙升得太高 - 比我的进程使用的要多得多。

我已将问题追溯到 FileStream.Write() 调用 - 当这条线被取出时,内存使用似乎按预期进行。我的 BigBufferSizeInBytes 是默认值(4k),我看不到这些可以收集的任何地方......

您在查看问题时发现的任何内容都将不胜感激!

于 2009-06-24T08:42:16.270 回答
0

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

另外 - 这是一个文件吗?单个 FileStream 不能保证是线程安全的......所以如果你不做同步,期待混乱。

于 2009-02-16T10:23:48.247 回答
0

这些限制可能来自底层操作系统,而 .NET Framework 无力克服这些限制。

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

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

更新:似乎 .NET 直到 4.0 才正式支持虚拟文件映射。您可能想查看此功能的第三方实现。

戴夫

于 2009-02-16T10:27:21.517 回答