damien.grandemange
unread,Jun 28, 2013, 9:41:03 AM6/28/13Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to jpos-...@googlegroups.com
Hi,
Lately, i have been trying to create a pool of channels (for a 'one shot' use of these channels) using Apache Commons Pool version 1.6.
Note that this thread is more a feedback actually. There's absolutely no issue with jPos channels and Apache Commons Pool. I rather want to focus on some inconvenience that occurred while trying to tight them.
In Apache Commons Pool, to create a pool of objects, one often uses the provided base implementation org.apache.commons.pool.impl.GenericObjectPool.
From this pool, a client code may borrow an instance when it needs it :
channel = channelPool.borrowObject();
and must return it to the pool once it's done with it :
channelPool.returnObject(channel);
Now, when creating the pool, one must provide a factory implementing org.apache.commons.pool.PoolableObjectFactory . This interface describes how to to create, destroy, passivate instances of the pool.
Base implementation org.apache.commons.pool.BasePoolableObjectFactory provides a no op implementation of these methods.
So, to create and initialize channel instances, one can provide his own factory implementation and override the BasePoolableObjectFactory.makeObject() method.
Eventually, when a new channel instance will be needed in the pool, pool internals will delegate channel instanciation to this method. If everything gets fine, a new channel will be available in the pool.
Working with Apache Commons Pool, one may want to configure its pool to hold a number of idle instances in a [minIdle, maxIdle] interval.
An eviction thread runs every timeBetweenEvictionRunsMillis milliseconds and chases instances stayed idle for more than minEvictableIdleTimeMillis milliseconds in the pool.
This can be done by providing a org.apache.commons.pool.impl.GenericObjectPool.Config when creating the pool.
Eventually, pool instanciation code could look like this :
poolConfig.maxActive = 25;
poolConfig.maxIdle = 12;
poolConfig.minIdle = 7;
poolConfig.minEvictableIdleTimeMillis = 120000;
poolConfig.numTestsPerEvictionRun = 25;
poolConfig.timeBetweenEvictionRunsMillis = 60000;
channelPool = new GenericObjectPool<ISOChannel>(poolFactory, poolConfig);
With such configuration, pool holds at least 7 idle instances in the pool. It also runs a=n eviction thread every minute to chase instances stayed idle in pool for more than 3 minutes.
I used this kind of configuration and it worked quite fine. Still ...
Looking at the JVM memory through the jconsole, memory graph showed that heap used memory was growing and growing, even though the app wasn't processing any dummy incoming requests. So what ?
Periodic manual GC never returned the memory back to it's lower level, and eventually, 12 hours later, boum ... the mercyless OutOfMemoryException came up.
So what happened ?
First, i must say that i wasn't aware at all that this memory leak was tighed to the <jPos channel, commonos pool> couple. So i many times ran the jmap java tool against my running app and compared successive class histograms.
Doing this, I noticed the [Lorg.jpos.iso.ISOFieldPackager instances number was keeping growing. How could that be ? After all, the app wasn't processing anything at all.
Wondering if my pool implementation was buggy, i put some extra logs in the BasePoolableObjectFactory.makeObject() method of my channel factory.
Looking at the instance creation frequency, I realized that, when the commons pool eviction thread runs, even if the pool holds a sufficient number of idle instances, any too older instances (age > minEvictableIdleTimeMillis) are re-created (that means destroyed and re-make).
One of the possible explanation of the memory leak was that the evicted channel instances were retained by some other object in the memory. My implementation wasn't doing retention, so i check the jPos QFactory, but there was nothing suspect there either.
Taking a look at Q2 logs, the sysmon dump was showing that the older channels were still listed : could it be that the channels were retained by the NameRegistrar ?
Looking deeper into jPos BaseChannel implementation, i saw that the setName(String name) setter method was also caring of channel instance registration in the NameRegistrar.
Well, don't know if there is a better way, but i eventually fixed this by overriding the pool's object factory BasePoolableObjectFactory.destroyObject(ISOChannel channel) method like this :
public void destroyObject(ISOChannel channel) throws Exception {
NameRegistrar.unregister("channel." + channel.getName());
}
No more leaks now. Phew ...