Question

I have an application that reads from large amount of MSMQ queues(about 10000 at the moment). I use queue.BeginPeek with UInt32.MaxValue timeout to receive message from queue. When the message appears in queue, I process it and call queue.BeginPeek again. So I listen to all the queues, but message processing is done on Thread Pool.

I noticed that memory usage slowly grows(two weeks of work cause growth from 200 MB to 800 MB). After investigating the dump file I see typical heap fragmentation picture with many free objects (some of them have size about several megabytes). And there are pinned objects between the holes.

This seems to be common situation when working with calls to unmanaged code, that create pinned objects. But I did not find any solution in the internet.

So is memory management in .NET so pure, that it does not allow to complete even such simple scenarios, or I miss something?

Edit : I've made some investigations in sample applications. The holes(free memory zones, so called free objects) between pinned objects are reused by GC when allocating memory for new objects. But in my production application, pinned objects are long living, and they finally appear in 2nd generation with the holes between them(because GC just shifts the border which separates generations). As I have really little normal long-living objects, I see this holes in 2nd generation in dump file.

So memory consumption of my application can grow to 10000*(average size of the hole). (10000 is the number of queues that can also increase in future). I have no ideas how to fix this at the moment. The only way is to restart application from time to time.

Once again I can only ask, why .NET does not have separate heap for pinned objects? (maybe this is newbie question). At the moment I see that calling async operations that work with unmanaged code can cause memory issues.

Was it helpful?

Solution

After looking at the source code for the managed wrapper for MSMQ it seems that you have stumbled upon a genuine problem using the API. Calling BeginPeek will create a collection of properties that are then pinned before passed to the unmanaged API. Only when a message is received are these properties unpinned but to continue receiving messages you have to call BeginPeek at this point leading to memory fragmentation over time.

If this fragmentation is a real concern the only hack I can come up with is every hour or so you should cancel all calls to BeginPeek, force a garbage collection and then resume normal listening operations. This should allow the garbage collector to handle the fragmentation.

OTHER TIPS

Well, if it is an issue with the pinned objects being long-lived then a simple work-around would be to use a timeout on the BeginPeek with a shorter duration to keep them from moving into the next generation. After each timeout and EndPeek you can reissue the BeginPeek. Depending on when the properties that Martin refered to are created and disposed, you may have to recreate the queue object (not the queue itself obviously, just the amanged wrapper). Though with luck you won't have to take it that far.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top