使用 MSMQ 异步 IO 时的 .NET 堆碎片

标签 .net msmq memory-fragmentation

我有一个从大量 MSMQ 队列(目前大约 10000 个)中读取的应用程序。
我用 queue.BeginPeek使用 UInt32.MaxValue 从队列接收消息超时。当消息出现在队列中时,我处理它并调用 queue.BeginPeek再次。所以我监听所有的队列,但是消息处理是在线程池上完成的。

我注意到内存使用量增长缓慢(两周的工作导致从 200 MB 增长到 800 MB)。在调查转储文件后,我看到了典型的堆碎片图片,其中包含许多空闲对象(其中一些具有大约几兆字节的大小)。孔之间有固定的物体。

在处理对创建固定对象的非托管代码的调用时,这似乎是常见情况。但我没有在互联网上找到任何解决方案。

那么 .NET 中的内存管理是否如此纯粹,以至于它甚至不允许完成这样简单的场景,或者我错过了什么?

编辑:我在示例应用程序中进行了一些调查。在为新对象分配内存时,固定对象之间的空洞(空闲内存区域,所谓的空闲对象)会被 GC 重用。
但是在我的生产应用程序中,固定对象是长期存在的,它们最终出现在第 2 代中,它们之间有孔(因为 GC 只是移动了分隔代的边界)。由于我几乎没有正常的长生命周期对象,因此我在转储文件中看到了第二代中的这些漏洞。

所以我的应用程序的内存消耗可以增长到 10000*(孔的平均大小)。 (10000 是将来也可以增加的队列数)。我目前不知道如何解决这个问题。唯一的方法是不时重新启动应用程序。

我再次只能问,为什么.NET 没有用于固定对象的单独堆? (也许这是新手问题)。目前我看到调用与非托管代码一起使用的异步操作会导致内存问题。

最佳答案

查看 MSMQ 托管包装器的源代码后,您似乎偶然发现了使用 API 的真正问题。调用 BeginPeek将创建一个属性集合,然后在传递给非托管 API 之前固定这些属性。只有当收到消息时,这些属性才会取消固定,但要继续接收消息,您必须调用 BeginPeek此时会导致内存碎片随着时间的推移。

如果这种碎片是一个真正的问题,我能想出的唯一黑客是每小时左右你应该取消对 BeginPeek 的所有调用。 ,强制垃圾收集,然后恢复正常的监听操作。这应该允许垃圾收集器处理碎片。

关于使用 MSMQ 异步 IO 时的 .NET 堆碎片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12674074/

相关文章:

c# - 隐式和显式委托(delegate)创建之间的区别(有和没有泛型)

c# - Winforms:Application.Exit 与 Environment.Exit 与 Form.Close

c++ - 避免内存碎片的方法

c# - 使用字节数组时的堆碎片

c - 检测进程中的内存碎片问题

c# - 我在线程方面做错了什么?

c# - 在堆上查找最接近给定地址的 .NET 对象 (.NET2.0/3.5)

windows-7 - Win2008 R2 上的 MSMQ 不会收到来自旧客户端的消息

c# - WCF 节流设置

c# - 使用 WCF 的 MSMQ 监听器