Discussion:
Memory leak in BeginPeek()?
(too old to reply)
Ed Kramer
2005-04-14 20:23:45 UTC
Permalink
I figured I would test to make sure that it's not some sort of weird remoting
voodoo ( since I am remoting in the above app ) and thus wrote a quick test
app. All the test app does is attach to a queue ( or create a queue if it
doesn't exit ), once set up it calls .BeginPeek() as above. There is
additional functionality added to allow me to toss a message into the queue
and retreive it but that's all she wrote. Nothing fancy there...

It's basically just...

queue.PeekCompleted+= new PeekCompletedEventHandler( queue_PeekCompleted);
queue.BeginPeek( timeout );

private void queue_PeekCompleted(object sender, PeekCompletedEventArgs e)
{
try
{
Console.Write("Taking a peek...");
queue.EndPeek( e.AsyncResult );
}
catch( MessageQueueException ex )
{
if ( ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout )
{
Console.WriteLine("Peek timeout");
}
}
finally
{
if ( ! this.queueShutdown )
queue.BeginPeek( PEEK_TIMEOUT, e.AsyncResult.AsyncState );
else
KillQueue( );
}
}


In the last hour the BeginPeek() has leaked 2,664 message objects and 2,673
ManualResetEvents.
Eugene Shvets [MSFT]
2005-04-14 21:20:24 UTC
Permalink
Ed,

Please attach your code (in as simple form as possible), and i will try to
repro it. What version of .Net framework are you using?

Thanks,
Eugene
Don't know if this means anything but from looking in the debugger
window...
the PeekCompletedEventArgs contains an something called
- [System.Messaging.MessageQueue.AsynchronousRequest]
This class, has as one of the properties of it, a message object. The
message object's properties seem to be largely exceptions of
SystemInvalidOperationException type though not all of them. Two of them
that
stand out to me are the onCompletionStatusChanged property ( which
contains a
System.Threading.IOCompletionCallback) and the resetEvent property ( which
contains an ManualResetEvent ). There is also a reference to a Message
directly attached to the PeekCompletedEventArgs class through the Message
property but it just shows up as an exception of MessageQueueException
type.
Oddly enough the IAsyncResult that I get back from the BeginPeek( ) has
the
same things in it except for no public accessor to the message object
that's
screwed up. My count of undisposed Message object always goes up on the
line
of .BeginPeek(). But I don't really know how to force this object to
dispose
of itself and not hang around. I tried to cast the IAsyncResult as the
AsynchronousRequest but the compiler doesn't think it exists ( a
non-public
class perhaps? ).
the .Net Memory profiler says that these objects root back to
System.Threading's IOCompletionCallback. and the the last thing callstack
says MessageQueue.ReceiveAsync( ).
Ed Kramer
2005-04-15 00:00:02 UTC
Permalink
Here you go... eMessage is basically just something I put in so I'd have
something to put into messages. It just contains two strings and isn't really
important.

using System;
using System.Messaging;

namespace MSMQTest
{
public class QueueHandler
{
private const int PEEK_TIMEOUT_SECONDS = 2;
private const int RECEIVE_TIMEOUT_SECONDS = 5;
private static TimeSpan PEEK_TIMEOUT = new TimeSpan(0,0,0,
PEEK_TIMEOUT_SECONDS, 0 );
private static TimeSpan RECEIVE_TIMEOUT = new TimeSpan(0,0,0,
RECEIVE_TIMEOUT_SECONDS, 0 );

public delegate void OnNewMessages( );
public event OnNewMessages NewMessages;

private MessageQueue queue;
public QueueHandler( string queueName )
{
//Attach to queue
AttachQueue( queueName );
}

private void AttachQueue( string queueName )
{
if ( MessageQueue.Exists( queueName ) )
{
Console.WriteLine("Attaching to queue " + queueName );
queue = new MessageQueue( queueName );
}
else
{
queue = MessageQueue.Create( queueName );
Console.WriteLine("Setting up queue " + queueName );
}


queue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl );
queue.Formatter = new XmlMessageFormatter( new Type[] { typeof( eMessage
) } );
queue.PeekCompleted+=new PeekCompletedEventHandler(queue_PeekCompleted);
Console.WriteLine("\r\nStarting peek loop");
queue.BeginPeek( PEEK_TIMEOUT );
}

bool queueShutdown;
public void DetatchQueue( )
{
queueShutdown = true;
}

private void KillQueue( )
{
queue.Close();
queue.Dispose();
}

public void Enqueue( eMessage eMsg )
{
Message msmqMessage = new Message( eMsg );
queue.Send( msmqMessage );
}

public eMessage Dequeue( )
{
eMessage results = null;
try
{
Message msg = queue.Receive( RECEIVE_TIMEOUT );
results = (eMessage) msg.Body;
}
catch( MessageQueueException e )
{
if ( e.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout )
{
Console.WriteLine("Dequeue Timeout");
}
}

return results;
}

private void queue_PeekCompleted(object sender, PeekCompletedEventArgs e)
{
try
{
Console.Write("Taking a peek...");
queue.EndPeek( e.AsyncResult );
FireNewMessagesEvent( );
}
catch( MessageQueueException ex )
{
if ( ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout )
{
Console.WriteLine("Peek timeout");
}
}
finally
{
if ( ! this.queueShutdown )
queue.BeginPeek( PEEK_TIMEOUT, e.AsyncResult.AsyncState );
else
KillQueue( );
}
}

private void FireNewMessagesEvent( )
{
eMessage msg = Dequeue();
while ( msg != null )
{
Console.WriteLine("Msg: " + msg.Message );

msg = Dequeue();
}

msg = null;
}
}
}
Post by Eugene Shvets [MSFT]
Ed,
Please attach your code (in as simple form as possible), and i will try to
repro it. What version of .Net framework are you using?
Eugene Shvets [MSFT]
2005-04-15 00:03:15 UTC
Permalink
Ed,

There's no leak. This explanation is taken from MSMQ FAQ section 18.9
(http://download.microsoft.com/download/b/d/3/bd3cbd9c-2fbe-4702-968e-123e370ff346/msmqfaqpub.doc#_Toc86118087):

Often developers feel that System.Messaging leak memory, even after Garbage
Collection is done. The following scenario shows legitimate code which seem
to leak memory:



Loop

· Instantiate new MessageQueue object. The queue itself is empty.

· Register a async peek delegate and call BeginPeek.

· Unregister the async peek delegate and Dispose the MessageQueue
Object.

End loop



There are two "implementation details" in this example which seem to cause
leak:

1. Unregistering an async peek (or receive) delegate handler merely remove
the delegate from the list of callbacks in the MessageQueue object. It
doesn't cancel the peek/receive operation. There is no way in MSMQ to cancel
a pending peek/receive operation other than closing the queue handle. See
above for more details. This means that all the Peek operations are still
pending in the MSMQ driver and the memory which was allocated for messages
properties is still alive, giving a false impression of leak. In the example
above, If you fill the queue with messages then peek return immediately and
there is no leak. Garbage collection will free all memory used for received
properties.

2. Dispose() of MessageQueue doesn't close the queue handle, unless (at
least) one of the following is true:

· You disable the global connection cache. (Note that connection
cache is disabled by default on .Net Framework 2.0, and enabled by default
prior to it)

· The queue was opened with DENY_RECEIVE sharing mode.

In all other cases, the queue handle is cached and is closed only on exit,
at finalization time. If you disable the connection cache then there is no
leak. To disable connection cache set the EnableConnectionCache property of
the MessageQueue class to "false". The queue handle is closed in each
iteration, canceling the peek operation. Eventually Garbage Collection will
free the memory used for messages properties.



Thanks,

Eugene
Don't know if this means anything but from looking in the debugger
window...
the PeekCompletedEventArgs contains an something called
- [System.Messaging.MessageQueue.AsynchronousRequest]
This class, has as one of the properties of it, a message object. The
message object's properties seem to be largely exceptions of
SystemInvalidOperationException type though not all of them. Two of them
that
stand out to me are the onCompletionStatusChanged property ( which
contains a
System.Threading.IOCompletionCallback) and the resetEvent property ( which
contains an ManualResetEvent ). There is also a reference to a Message
directly attached to the PeekCompletedEventArgs class through the Message
property but it just shows up as an exception of MessageQueueException
type.
Oddly enough the IAsyncResult that I get back from the BeginPeek( ) has
the
same things in it except for no public accessor to the message object
that's
screwed up. My count of undisposed Message object always goes up on the
line
of .BeginPeek(). But I don't really know how to force this object to
dispose
of itself and not hang around. I tried to cast the IAsyncResult as the
AsynchronousRequest but the compiler doesn't think it exists ( a
non-public
class perhaps? ).
the .Net Memory profiler says that these objects root back to
System.Threading's IOCompletionCallback. and the the last thing callstack
says MessageQueue.ReceiveAsync( ).
Frank Boyne
2005-04-15 17:57:49 UTC
Permalink
Post by Ed Kramer
queue.BeginPeek( PEEK_TIMEOUT, e.AsyncResult.AsyncState );
I think I'd change this to use the BeginPeek (TimeSpan) overload to see if
not passing in e.AsyncResult.AsyncState made a difference.

Not that I can explain why passing in e.AsyncResult.AsyncState would cause
the symptoms you are seeing.

Loading...