我有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循环并释放所有未使用的缓冲区?
答案 0 :(得分:2)
如果代码显示十个线程正在打开文件,那么任何时候最多只有十个未处理的FileStream对象。是的,FileStream 确实有一个内部缓冲区,您在代码中使用“BigBufferSizeInBytes”指定的大小。你能否透露确切的价值?如果这足够大(例如~100MB)那么它很可能是问题的根源。
默认情况下(即在构造时未指定数字时),此缓冲区为4kB,对于大多数应用程序而言通常都可以。一般来说,如果你真的关心磁盘写入性能,那么你可以将这个增加到几个100kB而不是更多。
但是,对于您的特定应用程序,这样做没有多大意义,因为在Dispose()FileStream对象之前,所述缓冲区永远不会包含超过16-30个字节的内容。
要回答您的问题,只有在运行GC后无法分配请求的内存时,才会抛出OutOfMemoryException。同样,如果缓冲区非常大,那么系统可能会留下足够的内存,而不是连续的块。这是因为大型对象堆永远不会被压缩。
答案 1 :(得分:1)
我已经几次提醒过这个人,但Large object heap可以将这个异常放在相当低的位置,看似你有足够的可用内存或应用程序正常运行。
我几乎完全按照你在这里所描述的内容进行了相当频繁的讨论。
您需要发布更多代码才能正确回答此问题。但是,我猜它也可能与潜在的万圣节问题有关(Spooky Dooky)。
您正在读取的缓冲区也可能是问题(同样是大型对象堆相关),您还需要提供有关循环中发生的事情的更多详细信息。我刚刚找到了我遇到的最后一个几乎完全相同的错误(我正在执行许多并行哈希更新,它们都需要在输入文件的读取中保持独立状态)....
OOP!刚滚动并注意到“BigBufferSizeInBytes”,我再次倾向于大型物体堆......
如果我是你,(由于缺乏上下文,这非常困难),我会提供一个小的调度“mbuf”,你复制进出,而不是允许你所有的独立线程分别阅读你的大型支持数组...(即很难不使用非常恶劣的代码语法导致不正确的分配)。
答案 2 :(得分:0)
通常不会在FileStream中分配缓冲区。也许问题是“编写成千上万的小缓冲区”这一行 - 你真的是这个意思吗?通常,您会多次,多次重复使用缓冲区(即,在不同的读/写调用中)。
另外 - 这是一个单独的文件吗?单个FileStream不保证是线程安全的...所以如果你没有进行同步,那么就会出现混乱。
答案 3 :(得分:0)
这些限制可能源于底层操作系统,并且.NET Framework无法克服这些限制。
我无法从您的代码示例中推断出您是否同时打开了很多这些FileStream对象,或者是否按顺序打开它们。使用'using'关键字将确保在fs.Write()调用后关闭文件。关闭文件不需要GC循环。
FileStream类实际上是针对文件的顺序读/写访问。如果您需要快速写入大文件中的随机位置,您可能需要查看使用虚拟文件映射。
更新:似乎虚拟文件映射在.NET中不会得到官方支持,直到4.0。您可能希望了解此功能的第三方实现。
戴夫
答案 4 :(得分:0)
我正在经历类似的事情,并想知道你是否曾压制过问题的根源?
我的代码在文件之间进行了大量的复制,在不同的字节文件之间传递了相当多的megs。我注意到虽然进程内存使用率保持在一个合理的范围内,但系统内存分配在复制过程中会出现过高的影响 - 比我的进程使用的内存分配要多得多。
我已经将问题跟踪到FileStream.Write()调用 - 当这行被取出时,内存使用似乎按预期进行。我的BigBufferSizeInBytes是默认值(4k),我无法看到这些可以收集的任何地方......
感谢您在查看问题时发现的任何事情都会感激不尽!