来自队列的大对象堆和字符串对象

Yan*_*nis 11 .net c# memory-leaks memory-management large-object-heap

我有一个Windows控制台应用程序应该运行几天和几个月没有重新启动.该应用程序从MSMQ检索"工作"并进行处理.有30个线程同时处理工作块.

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

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

我通过分析器运行应用程序并注意到大部分内存(可能是一个演出)在大对象堆中未使用但结构是分段的.

我发现这些未使用的(垃圾收集)字节中有90%是先前分配的字符串.我开始怀疑从MSMQ进来的字符串是分配,使用然后解除分配,因此是碎片的原因.

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

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

谢谢,Yannis

更新:关于如何检索这些"工作"块

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

目前我们在工作节点方面

WorkChunk chunk = _Queue.Receive();

所以在这个阶段我们几乎无法缓存.如果我们以某种方式改变了结构,那么我想我们可以做一些进步.无论如何,我们必须解决这个问题,以便我们做任何需要做的事情,以避免浪费数月的工作.

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

Jus*_*tin 4

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

大型对象堆被发现

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

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

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

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