我有一个 Windows 控制台应用程序,它应该可以运行数天和数月而无需重新启动。该应用程序从 MSMQ 检索“工作”并对其进行处理。有 30 个线程同时处理一个工作 block 。
来自 MSMQ 的每个工作 block 大约为 200kb,其中大部分分配在单个 String 对象中。
我注意到,在处理了大约 3-4 千个这些工作 block 之后,应用程序的内存消耗高得离谱,消耗了 1-1.5 GB 的内存。
我通过探查器运行该应用程序,并注意到大部分内存(可能是 gig 左右)在大型对象堆中未使用,但结构是碎片化的。
我发现这些未使用(垃圾收集)字节中有 90% 是以前分配的 String。然后我开始怀疑来自 MSMQ 的字符串被分配、使用然后被释放,因此是碎片的原因。
我知道像 GC.Collect(2 or GC.Max...) 这样的东西不会有帮助,因为它们 gc 大对象堆但不压缩它(这是这里的问题)。所以我认为我需要的是缓存这些字符串并以某种方式重新使用它们,但由于字符串是不可变的,我将不得不使用 StringBuilders。
我的问题是:有没有办法不改变底层结构(即使用 MSMQ,因为这是我无法改变的)并且仍然避免每次都初始化一个新的字符串以避免 LOH 碎片化?
谢谢, 雅尼斯
更新:关于当前如何检索这些“工作” block
目前这些在 MSMQ 中存储为 WorkChunk 对象。这些对象中的每一个都包含一个名为 Contents 的字符串和另一个名为 Headers 的字符串。这些是实际的文本数据。如果需要,我可以将存储结构更改为其他内容,如果需要,我可以将底层存储机制更改为 MSMQ 之外的其他内容。
目前我们在工作节点方面做
WorkChunk block = _Queue.Receive();
所以在这个阶段我们可以缓存的东西很少。如果我们以某种方式改变结构,那么我想我们可以取得一些进展。在任何情况下,我们都必须解决这个问题,所以我们将尽一切努力避免浪费数月的工作。
更新:我继续尝试了下面的一些建议,发现这个问题无法在我的本地机器(运行 Windows 7 x64 和 64 位应用程序)上重现。这让事情变得更加困难 - 如果有人知道为什么那么它真的有助于在本地重新记录这个问题。
最佳答案
您的问题似乎是由于大对象堆上的内存分配引起的 - 大对象堆未压缩,因此可能是碎片的来源。这里有一篇很好的文章更详细地介绍了一些调试步骤,您可以按照这些步骤确认大对象堆的碎片正在发生:
您似乎有两个三个解决方案:
- 改变您的应用程序以对 block /较短的字符串执行处理,其中每个 block 小于 85,000 字节 - 这避免了大对象的分配。
- 更改您的应用程序以预先分配一些大内存块,然后通过将新消息复制到分配的内存中来重新使用这些内存块。参见 Heap fragmentation when using byte arrays .
- 保持原样 - 只要您没有遇到内存不足异常并且应用程序不干扰系统上运行的其他应用程序,您就应该保持原样。
了解虚拟内存和物理内存之间的区别很重要 - 即使进程使用大量虚拟内存,如果分配的对象数量相对较少,那么它可能是物理内存使用进程很低(未使用的内存被分页到磁盘)意味着对系统上其他进程的影响很小。您可能还会发现“VM Hoarding”选项有帮助 - 阅读“发现大对象堆”一文以获取更多信息。
这两种更改都涉及更改您的应用程序以使用字节数组和短子字符串而不是单个大字符串来执行其部分或全部处理 - 这对您来说有多困难将取决于它是哪种处理你在做什么。
关于c# - 来自队列的大对象堆和字符串对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7766127/