[
https://issues.jboss.org/browse/JBRULES-3559?page=com.atlassian.jira.plug...
]
Fabio Strozzi edited comment on JBRULES-3559 at 7/10/12 11:21 AM:
------------------------------------------------------------------
Hi, I have some updates about the problems I reported.
There're two main issues involved here, some of which may have been fixed in Drools
5.4:
# a memory leakage happens when transaction management is done in a Oracle Weblogic 10.3
application server and no transaction manager is esplicitly set in the knowledge-base
environment.
# even if I can get around the first issue by esplicitly set a transaction manager,
another memory leakage happens due to an event listener being registered twice when a
persisted knowledge session is loaded while being removed only once when the session is
disposed (read ahead for the details).
In order to fix issue #1 class org.drools.persistence.jta.JtaTransactionManager must be
changed as follows:
# a new value has to be inserted into array FALLBACK_TRANSACTION_MANAGER_NAMES in order to
support the naming convention of Oracle Weblogic 10.3:
{code}
public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = new
String[]{"java:comp/TransactionManager",
"java:appserver/TransactionManager", "java:pm/TransactionManager",
"java:/TransactionManager", "javax.transaction.TransactionManager"};
{code}
# method findTransactionSynchronizationRegistry has to be fixed by changing these lines
{code}
InitialContext context = new InitialContext();
context.lookup(jndiName);
logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location
[{}]", jndiName);
return tsr;
{code}
into these
{code}
InitialContext context = new InitialContext();
Object tsr = context.lookup(jndiName);
logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location
[{}]", jndiName);
return tsr;
{code}
# moreover, if no transaction manager or transaction sysnchronizionation register were
found, then there's no need to update the synchronization map. A potential fix may be
obtained by changing method registerTransactionSynchronization of JtaTransactionManager as
follows:
{code}
public boolean registerTransactionSynchronization(final TransactionSynchronization ts) {
if ( this.tsr != null ) {
TransactionSynchronizationRegistryHelper.registerTransactionSynchronization(this.tsr,
ts);
return true;
} else if (this.tm != null) {
try {
this.tm.getTransaction().registerSynchronization(new
JtaTransactionSynchronizationAdapter(ts));
return true;
} catch (Exception e) {
// No JTA TransactionManager available - log a warning.
logger.warn("Participating in existing JTA transaction, but no JTA
TransactionManager or TransactionSychronizationRegistry available: ", e);
}
} else {
// No JTA TransactionManager available - log a warning.
logger.warn("Participating in existing JTA transaction, but no JTA
TransactionManager or TransactionSychronizationRegistry available: ");
}
return false;
}
{code}
Also, method registerRollbackSync of class SingleSessionCommandService must be changed
accordingly
{code}
private void registerRollbackSync() {
if ( synchronizations.get( this ) == null ) {
if (txm.registerTransactionSynchronization( new SynchronizationImpl( this ) ))
synchronizations.put( this, this );
}
}
{code}
Concerning issue #2, the memory leakage is due to this method
{code}InputMarshaller.readSession(MarshallerReaderContext context, int id, ExecutorService
executor, Environment environment, SessionConfiguration config){code}
when it performs the following steps:
{code}
ReteooStatefulSession session = new ReteooStatefulSession( id,
context.ruleBase,
executor,
handleFactory,
initialFactHandle,
propagationCounter,
config,
agenda,
environment );
session.setKnowledgeRuntime( new StatefulKnowledgeSessionImpl( session ) );
{code}
The constructor of StatefulKnowledgeSessionImpl sets the knowledge runtime once again;
this in turn instances a new ProcessRuntimeImpl class which adds an event listener by
using method initProcessEventListeners.
Whenever a new listener is added twice (see method addEventListener of KnowledgeBaseImpl)
a new KnowledgeBaseEventListenerWrapper object is created and then it's added to the
list ruleBase; also, the listener is added to the hashmap called
mappedKnowledgeBaseListeners. Problem comes because if addEventListener is called more
than one time for the same listener, then the map will contain only one reference to the
listener while the ruleBase list will contain two references to two different wrappers.
This becomes clear when the dispose method of ProcessRuntimeImpl is called and the
listener is removed from the knowledge base: every time this happens, only one wrapper is
removed from the ruleBase list hence causing the leakage.
I'm attaching two test apps that prove this fact: application AppOk always
instantiates a new session while AppKo loads an existing session.
One possible fix could be to rewrite the readSession method as done with Drools 5.4:
{code}
ReteooStatefulSession session = new ReteooStatefulSession( id,
context.ruleBase,
executor,
handleFactory,
initialFactHandle,
propagationCounter,
config,
agenda,
environment );
new StatefulKnowledgeSessionImpl( session ) ;
{code}
Even if issue #2 has been fixed in Drools 5.4, I cannot manage to update to this version
of the library as it would need several regression tests on my application.
In order to fix issue #1 is it safe to fix the JtaTransactionManager the way I described?
In order to avoid the memory leakage associated to issue #2, do you think it's
reasonable not to load sessions but always creating new ones? Could this cause performance
problems for instance?
Given that version 5.3.0 Final was a stable release, do you think a bug-fix release can be
expected?
Regards
Fabio
was (Author: f.strozzi):
Hi, I have some updates about the problems I reported.
There're two main issues involved here, some of which may have been fixed in Drools
5.4:
# a memory leakage happens when transaction management is done in a Oracle Weblogic 10.3
application server and no transaction manager is esplicitly set in the knowledge-base
environment.
# even if I can get around the first issue by esplicitly set a transaction manager,
another memory leakage happens due to an event listener being registered twice when a
persisted knowledge session is loaded while being removed only once when the session is
disposed (read ahead for the details).
In order to fix issue #1 class org.drools.persistence.jta.JtaTransactionManager must be
changed as follows:
# a new value has to be inserted into array FALLBACK_TRANSACTION_MANAGER_NAMES in order to
support the naming convention of Oracle Weblogic 10.3:
{code}
public static final String[] FALLBACK_TRANSACTION_MANAGER_NAMES = new
String[]{"java:comp/TransactionManager",
"java:appserver/TransactionManager", "java:pm/TransactionManager",
"java:/TransactionManager", "javax.transaction.TransactionManager"};
{code}
# method findTransactionSynchronizationRegistry has to be fixed by changing these lines
{code}
InitialContext context = new InitialContext();
context.lookup(jndiName);
logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location
[{}]", jndiName);
return tsr;
{code}
into these
{code}
InitialContext context = new InitialContext();
Object tsr = context.lookup(jndiName);
logger.debug("JTA TransactionSynchronizationRegistry found at default JNDI location
[{}]", jndiName);
return tsr;
{code}
# moreover, if no transaction manager or transaction sysnchronizionation register were
found, then there's no need to update the synchronization map. A potential fix may be
obtained by changing method registerTransactionSynchronization of JtaTransactionManager as
follows:
{code}
public boolean registerTransactionSynchronization(final TransactionSynchronization ts) {
if ( this.tsr != null ) {
TransactionSynchronizationRegistryHelper.registerTransactionSynchronization(this.tsr,
ts);
return true;
} else if (this.tm != null) {
try {
this.tm.getTransaction().registerSynchronization(new
JtaTransactionSynchronizationAdapter(ts));
return true;
} catch (Exception e) {
// No JTA TransactionManager available - log a warning.
logger.warn("Participating in existing JTA transaction, but no JTA
TransactionManager or TransactionSychronizationRegistry available: ", e);
}
} else {
// No JTA TransactionManager available - log a warning.
logger.warn("Participating in existing JTA transaction, but no JTA
TransactionManager or TransactionSychronizationRegistry available: ");
}
return false;
}
{code}
Also, method registerRollbackSync of class SingleSessionCommandService must be changed
accordingly
{code}
private void registerRollbackSync() {
if ( synchronizations.get( this ) == null ) {
if (txm.registerTransactionSynchronization( new SynchronizationImpl( this ) ))
synchronizations.put( this, this );
}
}
{code}
Concerning issue #2, the memory leakage is due to this method
{code}InputMarshaller.readSession(MarshallerReaderContext context, int id, ExecutorService
executor, Environment environment, SessionConfiguration config){code}
when it performs the following steps:
{code}
ReteooStatefulSession session = new ReteooStatefulSession( id,
context.ruleBase,
executor,
handleFactory,
initialFactHandle,
propagationCounter,
config,
agenda,
environment );
session.setKnowledgeRuntime( new StatefulKnowledgeSessionImpl( session ) );
{code}
The constructor of StatefulKnowledgeSessionImpl sets the knowledge runtime once again;
this in turn instances a new ProcessRuntimeImpl class which adds an event listener by
using method initProcessEventListeners.
Whenever a new listener is added twice (see method addEventListener of KnowledgeBaseImpl)
a new KnowledgeBaseEventListenerWrapper object is created and then it's added to the
list ruleBase; also, the listener is added to the hashmap called
mappedKnowledgeBaseListeners. Problem comes because if addEventListener is called more
than one time for the same listener, then the map will contain only one reference to the
listener while the ruleBase list will contain two references to two different wrappers.
This becomes clear when the dispose method of ProcessRuntimeImpl is called and the
listener is removed from the knowledge base: every time this happens, only one wrapper is
removed from the ruleBase list hence causing the leakage.
I'm attaching two test apps that prove this fact: application AppOk always
instantiates a new session while AppKo loads an existing session.
One possible fix could be to rewrite the readSession method as done with Drools 5.4:
{code}
ReteooStatefulSession session = new ReteooStatefulSession( id,
context.ruleBase,
executor,
handleFactory,
initialFactHandle,
propagationCounter,
config,
agenda,
environment );
new StatefulKnowledgeSessionImpl( session ) ;
{code}
Even if issue #2 has been fixed in Drools 5.4, I cannot manage to update to this version
of the library as it would need several regression tests on my application.
In order to fix issue #1 is it safe to fix the JtaTransactionManager the way I described?
In order to avoid the memory leakage associated to issue #2, do you think it's
reasonable not to load sessions but always creating new ones? Could this cause performance
problems for instance?
Regards
Fabio
Memory leakage when no transaction manager is used
--------------------------------------------------
Key: JBRULES-3559
URL:
https://issues.jboss.org/browse/JBRULES-3559
Project: Drools
Issue Type: Bug
Security Level: Public(Everyone can see)
Components: drools-core
Affects Versions: 5.3.1.Final, 5.4.0.Final
Environment: JBPM 5.2.Final, Drools 5.3.1.Final, Weblogic 10.3.5, JRockit
R28.2.2-7-148152-1.6.0_29-20111221-2104-linux-x86_64, Spring 3.0.5
Reporter: Fabio Strozzi
Assignee: Mark Proctor
Labels: drools-persistence-jpa, jbpm5, leak, memory, memory_leak,
memoryleak
Attachments: bpmLeak.zip
Hi,
a memory leak appears in Drools if a StatefulKnowledgeSession is created without a
transaction manager being speciifed (despite the fact that the dispose method is called
for each session, be it created or loaded).
To be more clear, this is the code that creates the session:
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
return JPAKnowledgeService.newStatefulKnowledgeSession(readKnowledgeBase(), null,
env);
When analyzing the memory dumps of a Spring-based webapplication that uses JBPM, under
stress conditions, the Eclipse Memory Analyzer blames
org.drools.persistence.SingleSessionCommandService class as the main cause of the leakage.
This class is taking up to the 46% of the whole heap by accumulating instances of
java.lang.Object[] arrays. These arrays are actually elements of a synchronized map called
sysnchronizations (see SingleSessionCommandService.java, line 73).
For my understanding of the source code of Drools, the sysnchronizations map is filled
every time the registerRollbackSync method of SingleSessionCommandService is called; on
the other hand, its elements are removed only if a JtaTransactionSynchronizationAdapter is
successfully registered against the transaction (via
javax.transaction.Transaction.registerSynchronization method).
If I understand it correctly, this happens only if a javax.transaction.TransactionManager
object is specified in the environment properties or one is found by
JtaTransactionManager.findTransactionManager.
When no transaction manager is present (or none is found by findTransactionManager),
method SynchronizationImpl.afterCompletion (the only one eligible to remove elements from
SingleSessionCommandService.synchronizations) is never called and memory increases with
each new session.
Please note that if no transaction synchronization manager is passed as an environement
property, then org.drools.persistence.jta.JtaTransactionManager never finds it, even if
the JNDI lookup successfully retrieves it; method findTransactionSynchronizationRegistry
never sets the tsr attribute, hence if it was null at the time the method is called, so it
is when it returns.
I also tried to set the transaction manager but I still get a constant memory
consumption. The Eclipse Memory Analyzer claims that, in this case, class
org.drools.reteoo.ReteooStatefulSession is suspected for the leakage.
As a final note, there's a minor issue in class JtaTransactionSynchronizationAdapter:
at line 31, the afterCompletion method is called on object this rather than on object ts
(TransactionSynchronization). This could cause a loop if status is STATUS_ACTIVE.
I'm working on a test case to reproduce the memory issues. I will provide one as soon
as possible along with a heap dump.
Fabio
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators:
https://issues.jboss.org/secure/ContactAdministrators!default.jspa
For more information on JIRA, see:
http://www.atlassian.com/software/jira