有趣的OutOfMemoryException与StringBuilder

时间:2009-11-20 09:50:27

标签: c# memory garbage-collection stringbuilder

我需要在循环中连续构建大字符串并将它们保存到数据库中,目前偶尔会产生OutOfMemoryException

这里基本上是基于某些数据使用XmlWriterStringBuilder创建字符串。然后我从外部库调用一个方法,将该xml字符串转换为其他字符串。之后,转换后的字符串将保存到数据库中。对于不同的数据,整个过程在一个循环中重复完成约100次。

字符串本身不是太大(每个低于500kBy)并且在此循环期间进程内存不会增加。但是,我偶尔会在OutOfMemeoryExcpetion内获得StringBuilder.Append。有趣的是,此异常不会导致崩溃。我可以捕获该异常并继续循环。

这里发生了什么?尽管系统中仍有足够的可用内存,为什么我会得到OutOfMemoryException?这是一些GC堆问题吗?

鉴于我无法避免转换所有这些字符串,我该怎样做才能使这项工作可靠?我应该强制GC收集吗?应该将Thread.Sleep放入循环中吗?我应该停止使用StringBuilder吗?应该只在面对OutOfMemoryException时重试?

3 个答案:

答案 0 :(得分:14)

有内存但没有可以处理字符串生成器大小的连续段。您必须知道,每次字符串生成器的缓冲区太短时,其大小都会加倍。如果您可以定义(在ctor中)构建器的大小,那就更好了。 完成大量对象后,您可以调用GC.Collect()

实际上,当你有一个OutOfMemory,它通常显示一个糟糕的设计,你可能使用硬盘驱动器(临时文件)而不是内存,你不应该反复分配内存(尝试重用对象/缓冲区/。 ..)。

我强烈建议您阅读Eric Lippert撰写的这篇文章“Out Of Memory” Does Not Refer to Physical Memory

答案 1 :(得分:3)

尝试在生成数据时重用StringBuilder对象。

使用之后或之前只需将StringBuilder的大小重置为0并开始追加。这将减少分配数量,并可能使OutOfMemory情况非常罕见。

说明我的观点:

void MainProgram()
{
    StringBuilder builder = new StringBuilder(2 * 1024); //2 Kb

    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
}

void PerformOperation(StringBuilder builder)
{
    builder.Length = 0;

    //
    // do the work here builder.Append(...);
    //
}

答案 2 :(得分:3)

根据您提到的尺寸,您可能会遇到Large Object Heap(LOH)碎片。

重用StringBuilder对象不是直接解决方案,您需要掌握底层缓冲区 如果可能,请事先计算或估算大小并预先分配。

如果你对向上分配进行整理可能会有所帮助,让我们说20k左右的倍数。这可以改善重用。