You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@qpid.apache.org by Fraser Adams <fr...@blueyonder.co.uk> on 2012/10/14 19:59:07 UTC

Qpid JMS setJMSReplyTo memory consumption.

Hello,
I'm currently implementing a QMF2 REST API and whilst working on the 
mechanism to invoke QMF2 methods I started messing around on some soak 
tests where I did a POST specifying a given QMF2 Object, which then does 
an invokeMethod on the QmfConsoleData on the REST Server.

What I noticed was that the memory was growing, so for a couple of weeks 
I was tearing my hair out thinking I had a bug somewhere in my code. 
Eventually I narrowed things down to a call to 
"request.setJMSReplyTo(destination);" in the invokeMethod() call in Console.

I then wrote a little cut down test to really hammer it, the guts of it 
are as follows:

             Connection connection = 
ConnectionHelper.createConnection(url, "{reconnect: true}");
             _console = new Console(this);
             _console.addConnection(connection);

             // First we create a large number of queues using the QMF2 
create method on the broker object
             List<QmfConsoleData> brokers = 
_console.getObjects("org.apache.qpid.broker", "broker");
             if (brokers.isEmpty())
             {
                 System.out.println("No broker QmfConsoleData returned");
                 System.exit(1);
             }

             QmfConsoleData broker = brokers.get(0);
             QmfData arguments = new QmfData();

             while (true)
             {
                 try
                 {
                     MethodResult results = 
broker.invokeMethod("getLogLevel", arguments);
                 }
                 catch (QmfException e)
                 { // This may be throw if we've already added the 
queues, we just catch and ignore for this test.
                     //System.out.println(e.getMessage());
                 }
             }

So it's just calling the getLogLevel method on the broker 
ManagementObject within a tight loop

I've attached a graph from jconsole showing the memory consumption over 
time.

As you can see it's not quite a leak rather it eventually settles down, 
though when the consumption "converges" CPU utilisation climbs to ~100% 
so for high rate request/response calls this could be an issue.

I've tracked this behaviour down to the use of 
"java.lang.ref.SoftReference" in AMQMessageDelegate_0_10.java in 
qpid.client.message


Although this isn't truly a memory leak it does seem to be less than 
ideal behaviour, in my case I've actually only got a couple of 
Destination objects but setJMSReplyTo is creating SoftReference 
instances on each call, which frankly seems excessive for most 
request/response use-cases as these SoftReferences stick around for 
quite a while.

I'd have thought that something like a proper LRU cache would have been 
more efficient (and less memory hungry), indeed something simple such as 
having a custom class extending TimerTask to wrap the Destination and 
ReplyTo so that when the TimerTask run() is invoked it can expire itself 
if it hasn't been used. As most request/response use-cases involve quite 
a high temporal correlation between the request and response a 
relatively modest expire time of say a few minutes would be sufficient 
and indeed if the time between request and response is high it's fairly 
likely that the cache isn't going to cause any significant performance 
gain anyway.

Thoughts??

BTW This was observed in qpid 0.12 so I guess things may have changed in 
later versions? 0.8 didn't use a SoftReference it looks like it was 
using a custom ReferenceMap

Regards,
Frase










Re: Qpid JMS setJMSReplyTo memory consumption.

Posted by Robbie Gemmell <ro...@gmail.com>.
 Changes were made in this area via
https://issues.apache.org/jira/browse/QPID-3440 which puts them in 0.16 and
0.18 (or the 0.20 alpha due later in the week) if you want to give them a
try.

Robbie

On 14 October 2012 18:59, Fraser Adams <fr...@blueyonder.co.uk>wrote:

> Hello,
> I'm currently implementing a QMF2 REST API and whilst working on the
> mechanism to invoke QMF2 methods I started messing around on some soak
> tests where I did a POST specifying a given QMF2 Object, which then does an
> invokeMethod on the QmfConsoleData on the REST Server.
>
> What I noticed was that the memory was growing, so for a couple of weeks I
> was tearing my hair out thinking I had a bug somewhere in my code.
> Eventually I narrowed things down to a call to "request.setJMSReplyTo(**destination);"
> in the invokeMethod() call in Console.
>
> I then wrote a little cut down test to really hammer it, the guts of it
> are as follows:
>
>             Connection connection = ConnectionHelper.**createConnection(url,
> "{reconnect: true}");
>             _console = new Console(this);
>             _console.addConnection(**connection);
>
>             // First we create a large number of queues using the QMF2
> create method on the broker object
>             List<QmfConsoleData> brokers = _console.getObjects("org.**apache.qpid.broker",
> "broker");
>             if (brokers.isEmpty())
>             {
>                 System.out.println("No broker QmfConsoleData returned");
>                 System.exit(1);
>             }
>
>             QmfConsoleData broker = brokers.get(0);
>             QmfData arguments = new QmfData();
>
>             while (true)
>             {
>                 try
>                 {
>                     MethodResult results = broker.invokeMethod("**getLogLevel",
> arguments);
>                 }
>                 catch (QmfException e)
>                 { // This may be throw if we've already added the queues,
> we just catch and ignore for this test.
>                     //System.out.println(e.**getMessage());
>                 }
>             }
>
> So it's just calling the getLogLevel method on the broker ManagementObject
> within a tight loop
>
> I've attached a graph from jconsole showing the memory consumption over
> time.
>
> As you can see it's not quite a leak rather it eventually settles down,
> though when the consumption "converges" CPU utilisation climbs to ~100% so
> for high rate request/response calls this could be an issue.
>
> I've tracked this behaviour down to the use of
> "java.lang.ref.SoftReference" in AMQMessageDelegate_0_10.java in
> qpid.client.message
>
>
> Although this isn't truly a memory leak it does seem to be less than ideal
> behaviour, in my case I've actually only got a couple of Destination
> objects but setJMSReplyTo is creating SoftReference instances on each call,
> which frankly seems excessive for most request/response use-cases as these
> SoftReferences stick around for quite a while.
>
> I'd have thought that something like a proper LRU cache would have been
> more efficient (and less memory hungry), indeed something simple such as
> having a custom class extending TimerTask to wrap the Destination and
> ReplyTo so that when the TimerTask run() is invoked it can expire itself if
> it hasn't been used. As most request/response use-cases involve quite a
> high temporal correlation between the request and response a relatively
> modest expire time of say a few minutes would be sufficient and indeed if
> the time between request and response is high it's fairly likely that the
> cache isn't going to cause any significant performance gain anyway.
>
> Thoughts??
>
> BTW This was observed in qpid 0.12 so I guess things may have changed in
> later versions? 0.8 didn't use a SoftReference it looks like it was using a
> custom ReferenceMap
>
> Regards,
> Frase
>
>
>
>
>
>
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@qpid.apache.org
> For additional commands, e-mail: users-help@qpid.apache.org
>