【发布时间】:2012-09-22 08:23:05
【问题描述】:
我有一个从大量 MSMQ 队列(目前大约 10000 个)读取的应用程序。
我使用queue.BeginPeek 和 UInt32.MaxValue 超时从队列接收消息。当消息出现在队列中时,我对其进行处理并再次调用queue.BeginPeek。所以我听所有的队列,但是消息处理是在线程池上完成的。
我注意到内存使用量增长缓慢(两周的工作导致从 200 MB 增长到 800 MB)。在调查了转储文件后,我看到了带有许多空闲对象的典型堆碎片图片(其中一些有大约几兆字节的大小)。并且孔之间有被钉住的物体。
在处理创建固定对象的非托管代码调用时,这似乎很常见。但是我在互联网上没有找到任何解决方案。
那么 .NET 中的内存管理是否如此纯粹,以至于它甚至无法完成如此简单的场景,或者我错过了什么?
编辑:我对示例应用程序进行了一些调查。在为新对象分配内存时,固定对象之间的空洞(空闲内存区域,所谓的空闲对象)被 GC 重用。 但是在我的生产应用程序中,固定对象是长期存在的,它们最终出现在第二代,它们之间有孔(因为 GC 只是移动了分隔代的边界)。由于我几乎没有正常的长寿命对象,因此我在转储文件的第二代中看到了这个漏洞。
所以我的应用程序的内存消耗可以增长到 10000*(孔的平均大小)。 (10000 是将来还可以增加的队列数)。我目前不知道如何解决这个问题。唯一的方法是不时重启应用程序。
我只能再次问,为什么 .NET 没有用于固定对象的单独堆? (也许这是新手问题)。目前我发现调用使用非托管代码的异步操作可能会导致内存问题。
【问题讨论】:
-
是的,当您有 10,000 次 BeginPeek() 调用时,会发生很多固定。不要期待奇迹。增长并没有多大意义,只有当它最终无法稳定并导致应用程序因 OOM 崩溃时,您才会遇到问题。
-
我编辑了我的帖子,我得到了线性增长(两周内从 200MB 增长到 800MB)。我不确定我什么时候会得到 OOM,如果这会发生,但这仍然是巨大的内存量。我只能重复一遍——我认为这种情况很典型。我是内存管理工具的新手,但为什么他们不能为固定对象实现单独的堆?另一种选择是使用 OverlappedData 结构创建缓存,但由于 System.Messaging 不是开源的,因此很难实现这种行为。使用当前的 GC 实现,固定对象只能在简单的场景中使用
-
您对 BeginPeek 有多少并发调用。你总是打电话给 EndPeek,对吧?在我看来,你不应该留下任何东西来让系统静默(没有未完成的呼叫)。
-
因为我总是需要监听队列,所以我在处理完消息后再次调用 BeginPeek。所以我总是有 10000 个并发 BeginPeek。
-
固定对象不会放在单独的堆上,因为它们一开始就没有固定。它们仅在调用需要在调用期间访问内存的非托管函数时被固定。在您的情况下,这些非托管调用是长期存在的,这意味着您有许多固定对象。
标签: .net msmq memory-fragmentation