我需要在循环中连续构建大字符串并将它们保存到数据库中,目前偶尔会产生OutOfMemoryException
。
这里基本上是基于某些数据使用XmlWriter
和StringBuilder
创建字符串。然后我从外部库调用一个方法,将该xml字符串转换为其他字符串。之后,转换后的字符串将保存到数据库中。对于不同的数据,整个过程在一个循环中重复完成约100次。
字符串本身不是太大(每个低于500kBy)并且在此循环期间进程内存不会增加。但是,我偶尔会在OutOfMemeoryExcpetion
内获得StringBuilder.Append
。有趣的是,此异常不会导致崩溃。我可以捕获该异常并继续循环。
这里发生了什么?尽管系统中仍有足够的可用内存,为什么我会得到OutOfMemoryException
?这是一些GC堆问题吗?
鉴于我无法避免转换所有这些字符串,我该怎样做才能使这项工作可靠?我应该强制GC收集吗?应该将Thread.Sleep
放入循环中吗?我应该停止使用StringBuilder
吗?应该只在面对OutOfMemoryException
时重试?
答案 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左右的倍数。这可以改善重用。