11

我有一个 Windows 控制台应用程序,它应该可以运行数天和数月而无需重新启动。该应用程序从 MSMQ 检索“工作”并对其进行处理。有 30 个线程同时处理一个工作块。

来自 MSMQ 的每个工作块大约为 200kb,其中大部分分配在单个 String 对象中。

我注意到在处理了大约 3-4 千个这样的工作块之后,应用程序的内存消耗非常高,消耗了 1 - 1.5 gb 的内存。

我通过分析器运行应用程序并注意到大部分内存(可能是 gig 左右)在大型对象堆中未使用,但结构是碎片化的。

我发现这些未使用的(垃圾收集的)字节中有 90% 是以前分配的字符串。那时我开始怀疑来自 MSMQ 的字符串被分配、使用然后解除分配,因此是碎片的原因。

我知道像 GC.Collect(2 or GC.Max...) 这样的东西不会有帮助,因为它们 gc 大对象堆但不压缩它(这是这里的问题)。所以我认为我需要缓存这些字符串并以某种方式重新使用它们,但由于字符串是不可变的,我将不得不使用 StringBuilders。

我的问题是:无论如何不改变底层结构(即使用MSMQ,因为这是我无法改变的)并且仍然避免每次都初始化一个新的字符串以避免分割LOH?

谢谢, 雅尼斯

更新:关于当前如何检索这些“工作”块

目前,这些在 MSMQ 中存储为 WorkChunk 对象。这些对象中的每一个都包含一个名为 Contents 的字符串和另一个名为 Headers 的字符串。这些是实际的文本数据。如果需要,我可以将存储结构更改为其他内容,如果需要,我可以将底层存储机制更改为 MSMQ 以外的其他内容。

目前我们在工作节点方面

WorkChunk 块 = _Queue.Receive();

所以在这个阶段我们可以缓存的东西很少。如果我们以某种方式改变结构,那么我想我们可以取得一些进展。无论如何,我们都必须解决这个问题,所以我们将尽一切可能避免浪费数月的工作。

更新:我继续尝试以下一些建议,并注意到无法在我的本地计算机上重现此问题(运行 Windows 7 x64 和 64 位应用程序)。这让事情变得更加困难 - 如果有人知道为什么,那么它真的会帮助在本地重新解决这个问题。

4

4 回答 4

4

您的问题似乎是由于大对象堆上的内存分配 - 大对象堆未压缩,因此可能是碎片的来源。这里有一篇很好的文章,它更详细地介绍了一些调试步骤,您可以按照这些步骤来确认正在发生大对象堆的碎片:

发现大对象堆

您似乎有两个三个解决方案:

  1. 更改您的应用程序以对块/较短的字符串执行处理,其中每个块小于 85,000 字节 - 这避免了大对象的分配。
  2. 更改您的应用程序以预先分配一些大块内存,然后通过将新消息复制到分配的内存中来重新使用这些块。请参阅使用字节数组时的堆碎片
  3. 保持原样 - 只要您没有遇到内存不足异常并且应用程序不干扰系统上运行的其他应用程序,您可能应该保持原样。

这里重要的是要了解虚拟内存和物理内存之间的区别——即使进程正在使用大量的虚拟内存,如果分配的对象数量相对较少,那么可能是该进程的物理内存使用率较低(未使用的内存被分页到磁盘)意味着对系统上其他进程的影响很小。您可能还会发现“VM Hoarding”选项有帮助 - 阅读“Large Object Heap Uncovered”一文了解更多信息。

任何一种更改都涉及更改您的应用程序以使用字节数组和短子字符串而不是单个大字符串来执行其部分或全部处理 - 这对您来说有多困难取决于您正在执行的处理类型.

于 2011-10-14T11:08:07.170 回答
2

当 LOH 上有碎片时,就意味着上面有分配的对象。如果您可以承受延迟,您可以偶尔等到所有当前正在运行的任务完成并调用GC.Collect(). 当没有引用的大对象时,它们都会被收集起来,有效地去除了 LOH 的碎片。当然,这仅在(几乎)所有大对象都未引用时才有效。

此外,迁移到 64 位操作系统也可能会有所帮助,因为在 64 位系统上,由于碎片导致的内存不足不太可能成为问题,因为虚拟空间几乎是无限的。

于 2011-10-14T11:25:32.677 回答
1

也许您可以创建一个字符串对象池,您可以在处理工作时使用它,然后在完成后返回。

一旦在 LOH 中创建了一个大对象,它就不能被删除 (AFAIK),所以如果你无法避免创建这些对象,那么最好的计划是重用它们。

如果您可以在两端更改协议,那么将您的“内容”字符串减少为一组较小的字符串(每个 <80k)应该可以阻止它们存储在 LOH 中。

于 2011-10-14T10:32:20.187 回答
0

如何使用 String.Intern(...) 消除重复引用。它有性能损失,但取决于您的字符串,它可能会产生影响。

于 2012-10-08T19:10:09.533 回答