有趣的OutOfMemoryException与StringBuilder
我需要在循环中不断构建大字符串并将它们保存到数据库中,目前偶尔会产生OutOfMemoryException
。
这里基本上是基于一些数据使用XmlWriter
和StringBuilder
创建一个字符串。 然后我从外部库调用一个方法,将该xml字符串转换为其他字符串。 之后,转换后的字符串将保存到数据库中。 对于不同的数据,这整个过程在一个循环中重复完成约100次。
字符串本身不是太大(每个低于500kBy)并且在此循环期间进程内存不会增加。 但是,偶尔我会在StringBuilder.Append
得到一个OutOfMemeoryExcpetion
。 有趣的是,此exception不会导致崩溃。 我可以捕获该exception并继续循环。
这里发生了什么? 尽管系统中仍有足够的可用内存,为什么我会得到一个OutOfMemoryException
? 这是一些GC堆问题吗?
鉴于我无法绕过转换所有这些字符串,我能做些什么来使这项工作可靠? 我应该强制GC收集吗? 应该将Thread.Sleep
放入循环中吗? 我应该停止使用StringBuilder
吗? 应该只是在面对OutOfMemoryException
时重试?
有内存但没有连续的段可以处理字符串生成器的大小。 您必须知道,每次字符串生成器的缓冲区太短时,其大小都会加倍。 如果您可以定义(在ctor中)构建器的大小,那就更好了。 完成大量对象后,可以调用GC.Collect()
。
实际上,当你有一个OutOfMemory,它通常显示一个糟糕的设计,你可能使用硬盘驱动器(临时文件)而不是内存,你不应该一次又一次地分配内存(尝试重用对象/缓冲区/ …) 。
我强烈建议你阅读这篇文章“Out of Memory”并不是指 Eric Lippert的物理内存 。
在进行数据生成时尝试重用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(...); // }
根据您提到的尺寸,您可能会遇到大对象堆 (LOH)碎片。
重用StringBuilder对象不是直接的解决方案,您需要掌握底层缓冲区。
如果可能,请事先计算或估算大小并预先分配。
如果你整理分配可能会有所帮助,让我们说20k左右的倍数。 这可以改善重用。
- Linq to Entities(EF 4.1):如何使用中间的通配符(’%term%term%’)执行SQL LIKE?
- 使用Socket.BeginAccept / EndAccept进行多个连接
- 绑定到窗口高度和宽度的问题
- 客户服务器套接字C#
- 在C#中检测多个同时按键
- 如何使代码约定忽略特定的程序集引用?
- 当DataContext发生更改时,WPF绑定OneWayToSource将source属性设置为“”
- 未处理的exception未被Global.asaxerror handling程序或自定义IHttpModuleerror handling程序捕获
- ASP.NET 5项目不正确地针对.NET 4.6.1并且无法调试