StringBuilder 的有趣 OutOfMemoryException
我需要在循环中不断构建大字符串并将它们保存到数据库中,目前偶尔会产生 OutOfMemoryException
。
这里基本上发生的事情是我根据一些数据使用 XmlWriter
和 StringBuilder
创建一个字符串。然后,我调用外部库中的一个方法,将该 xml 字符串转换为其他字符串。之后,转换后的字符串将保存到数据库中。对于不同的数据,整个过程会循环重复执行大约 100 次。
字符串本身并不太大(每个字符串低于 500kByte),并且在此循环期间进程内存不会增加。但是,偶尔我会在 StringBuilder.Append
中收到 OutOfMemeoryExcpetion
。有趣的是,这个异常不会导致崩溃。我可以捕获该异常并继续循环。
这是怎么回事?尽管系统中仍然有足够的可用内存,为什么我会收到 OutOfMemoryException
?这是 GC 堆问题吗?
鉴于我无法避免转换所有这些字符串,我该怎么做才能使其可靠地工作?我应该强制GC收集吗?应该将 Thread.Sleep 放入循环中吗?我应该停止使用 StringBuilder 吗?遇到 OutOfMemoryException 时是否应该简单地重试?
I have the need to continuously build large strings in a loop and save them to database which currently occasionally yields an OutOfMemoryException
.
What is basically going on here is I create a string using XmlWriter
with StringBuilder
based on some data. Then I call a method from an external library that converts this xml string to some other string. After that the converted string is saved to the database. This whole thing is done repeatedly in a loop about a 100 times for different data.
The strings by itself are not too big (below 500kByte each) and the process memory is not increasing during this loop. But still, occasionally I get a OutOfMemeoryExcpetion
within StringBuilder.Append
. Interestingly this exception does not result in a crash. I can catch that exception and continue the loop.
What is going on here? Why would I get an OutOfMemoryException
although there is still enough free memory available in the system? Is this some GC heap problem?
Given that I can't circumvent converting all these strings, what could I do to make this work reliably? Should I force a GC collection? Should put a Thread.Sleep
into the loop? Should I stop using StringBuilder
? Should simply retry when confronted with a OutOfMemoryException
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
有内存,但没有可以处理字符串生成器大小的连续段。你要知道,每次字符串构建器的缓冲区太短时,它的大小都会加倍。如果您可以(在 ctor 中)定义构建器的大小,那就更好了。
当您处理完大量对象后,您可以调用
GC.Collect()
。实际上,当你遇到内存不足时,它通常显示出糟糕的设计,你可以使用硬盘驱动器(临时文件)而不是内存,你不应该一次又一次地分配内存(尝试重用对象/缓冲区/...) 。
我强烈建议您阅读这篇文章 “内存不足”并非指物理内存,来自 Eric Lippert。
There is memory but no contiguous segment that can handle the size of your string builder. You have to know that each time the buffer of the string builder is too short, its size is doubled. If you can define (in the ctor) the size of your builder, it's better.
You MAY call
GC.Collect()
when you are done with a large collection of objects.Actually, when you have an OutOfMemory, it generaly shows a bad design, you may use the hard drive (temp files) instead of memory, you shouldn't allocate memory again and again (try to reuse objects/buffers/...).
I STRONGLY advice you to read this post “Out Of Memory” Does Not Refer to Physical Memory from Eric Lippert.
在进行数据生成时尝试重用 StringBuilder 对象。
使用之后或之前只需将 StringBuilder 的大小重置为 0 并开始追加。这将减少分配数量,并可能使内存不足的情况非常罕见。
为了说明我的观点:
Try to reuse StringBuilder object when you do data generation.
After or before use just reset the size of the StringBuilder to 0 and start appending. This will decrease number of allocations and possibly make OutOfMemory situation very rare.
To illustrate my point:
根据您提到的大小,您可能会遇到 大型对象堆 ( LOH)碎片。
重用 StringBuilder 对象不是直接的解决方案,您需要掌握底层缓冲区。
如果可能,请预先计算或估计大小并预先分配。
如果您向上分配,比如 20k 左右的倍数,这会有所帮助。这可以改善重用。
With the sizes you mention you are probably running into Large Object Heap (LOH) fragmentation.
Reusing StringBuilder objects is not a direct solution, you need to get a grip on the underlying buffers.
If possible, calculate or estimate the size beforehand and pre-allocate.
And it could help if you round up allocations, let's say to multiples of 20k or so. That could improve reuse.