处理大字符串,这是大对象堆碎片问题吗?

7

我有一个.NET 3.5应用程序。

  • 一个函数运行了一百万次
  • 它在1MB+字符串(不同大小的字符串)中执行搜索、替换和正则表达式操作

当我对应用程序进行分析时,我可以确认这些字符串存储在LOH中,但稍后被GC回收,因此在给定时间内只有最多10个字符串在LOH中(10个线程正在运行)。

我的理解是,这些大字符串位于LOH中,然后由于它们的分配位置(以及位于LOH中,因此不会被压缩),尽管操作中没有内存泄漏,但仍会导致碎片化。在 ~100K 次时不会出问题,但当达到1M+时会导致内存不足异常。

我正在使用ANTS Memory Profiler,以下是我在早期执行中得到的结果:

.NET Using 70MB of 210MB total private bytes allocated in to the application
Number of Fragments: 59
Number of Large Fragments : 48 (99.6% of free memory)
Largest Fragment: 9MB
Free Space: 52% of total memory  (37MB)
Unmanaged Memory: 66% of total private memory (160MB)
  1. 根据手头的数据,你是否认为我的诊断是正确的?
  2. 如果是,我该如何解决这个LOH碎片化问题?我必须处理那些字符串,并且它们都是很大的字符串。我应该找到一种方式将它们分开进行处理吗?在这种情况下,在拆分后的字符串中运行正则表达式等操作会非常具有挑战性。

1
另一个可能的解决方案是:创建一个单独的进程来进行字符串处理,并为每个字符串使用一个新进程(或者如果对于您的字符串来说每100K看起来很好等等)。每个进程都从干净的状态开始。这就是IIS回收应用程序池的原因之一 - 碎片化。 - vcsjones
@vcsjones,我在实际执行之前就想到了这个问题,但是觉得可能会过度解决 :) 我想确保这是问题的原因。我对GC的细节还不够熟悉,所以我不想浪费时间去解决一个假问题!问题是 reproducing(重现) 实际问题非常困难,如果我很幸运,可能需要一两天时间。因此,大部分时间我会相信性能分析工具的结果。 - dr. evil
2
你可以尝试以64位模式运行程序。这将解决问题,因为虚拟空间更大。 - xanatos
1
我知道你要求的是3.5版本,但是看这里:http://connect.microsoft.com/VisualStudio/feedback/details/521147/large-object-heap-fragmentation-causes-outofmemoryexception 主程说他们在4.0版本中部分修复了这个问题。 - xanatos
你知道如果在x64上运行但是编译成x86会发生什么吗? - dr. evil
我认为它不会改变。你需要重新编译...或者尝试我写的4.0版本。(你忘记回复你的评论了...) - xanatos
1个回答

2

是的。听起来没错。LOH被分段了,这导致运行时无法为大字符串分配足够的连续空间。
你有几个选择,我想做最简单和有效的选择应该是你应该选择的。这完全取决于它的写法。
1.将你的字符串分成足够小的块,使它们不在LOH中(小于85K - 注意:当对象被放在LOH上时,逻辑并不那么明确)。这将允许垃圾回收器能够回收空间。这绝不保证修复碎片化——否则肯定还会发生。如果你把字符串变小,但仍然在LOH上,你就会拖延问题。这取决于你需要处理多少超过100万个字符串。另一个缺点是——你仍然需要加载内存中的字符串来进行切割,因此它最终还是在LOH上。你必须在应用程序加载它们之前缩小字符串。有点进退两难。
编辑:Gabe 在评论中指出,如果你可以首先将你的字符串加载到 StringBuilder 中,在底层它会尽力避免将东西放入 LOH(直到你调用ToString )。
2.将字符串处理拆分成单独的进程。使用进程而不是线程。使用每个进程来处理,比如说,10K个字符串,然后杀死进程并启动另一个进程。这样,每个进程都从一个干净的状态开始。这样做的好处是它不会改变你的字符串处理逻辑(以防你不能让你的字符串更小进行处理),并避免了#1中的进退两难。缺点是这可能需要对你的应用程序进行较大的更改,并在主进程和子进程之间协调工作。诀窍是主进程只能告诉它大字符串在哪里,而不能直接将它给它,否则你又回到了进退两难的境地。

3
通常情况下,你可以将字符串加载到StringBuilder中,它(至少在.NET 4.0中)非常小心地避免了大对象堆。这将使得分解字符串变得容易。不幸的是,你不能在StringBuilder上运行正则表达式,并且在分解后的字符串上运行所需的正则表达式可能也不可行。 - Gabe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接