07-25-2014 03:30 AM
ProcessEngine
, each connected to a different datasource (one for each company branch), using H2 file database (for testing).ProcessEngine
uses about 4 MB of memory, including about 3.6 MB for MyBatis Configuration
(determined through heap dump analysis).Configuration
is stable over time, it is a big waste of memory: each ProcessEngine
may share the same configuration.sqlFragments
: the Configuration.getSqlFragments()
's call hierarchy has been examinated and all calls come from XML parsers and configuration-related methods (i.e. no query or other runtime methods). Thus, reusing sqlFragments
should be safe.mappedStatements
: the Configuration.getMappedStatements()
's call hierarchy has been examinated and all calls come from XML parsers and configuration-related methods (i.e. no query or other runtime methods). One manipulated data "databaseId" suggests that there is something specific to the database in the configuration but this flag is not used in Activiti configuration. Thus, reusing mappedStatements
should be safe.resultMaps
: I found a post from Clinton Begin (MyBatis's principal developper) on the MyBatis mailing list: "By definition the result mappings should be deterministic and consistent. They shouldn't really change on a per-request basis.". Thus reusing resultMaps
should be safe.Configuration
fields amongst all ProcessEngine
's. It is called after calling the ProcessEngineConfigurationImpl.buildProcessEngine()
:
private static final Logger LOGGER = Logger.getLogger("MyLogger");
/**
* Shared ORM configuration objects.
*/
private static Map<String, Object> sharedOrmConfigurationObjects = new HashMap<String, Object>();
/**
* Reduce the memory footprint of the underlying ORM configuration.
* The ORM configuration elements are shared for each Activiti ProcessEngineConfigurationImpl provided.
* This is an experimental feature.
* @param wfc the Activiti configuration
*/
private static void reduceOrmMemoryFootprint(final ProcessEngineConfigurationImpl wfc) {
SqlSessionFactory ssf = wfc.getSqlSessionFactory();
if (ssf != null) {
Configuration configuration = ssf.getConfiguration(); // the configuration is null if called before wfc.buildProcessEngine()
if (configuration != null) {
shareConfigurationObject(configuration, "sqlFragments");
// Implementation note: the Configuration.getSqlFragments()'s call
// hierarchy has been examinated and all calls come from XML parsers
// and configuration-related methods (i.e. no query or other runtime
// methods). Thus, reusing sqlFragments should be safe.
shareConfigurationObject(configuration, "mappedStatements");
// Implementation note: the Configuration.getMappedStatements()'s
// call hierarchy has been examinated and all calls come from XML
// parsers and configuration-related methods (i.e. no query or
// other runtime methods). One manipulated data "databaseId"
// suggests that there is something specific to the database in
// the configuration but this flag is not used in Activiti
// configuration. Thus, reusing mappedStatements should be safe.
shareConfigurationObject(configuration, "resultMaps");
// Implementation note: found on the MyBatis mailing list:
//
// "By definition the result mappings should be
// deterministic and consistent. They shouldn't
// really change on a per-request basis.".
// Clinton Begin (principal developper)
//
// Source: <a href='https://groups.google.com/d/topic/mybatis-user/-RG2pgNEtfI/discussion' rel='no-follow'>link</a>
}
}
}
/**
* Share a specific field of the MyBatis Configuration.
* @param configuration the current configuration
* @param sharedObjectFieldName the field to be shared
*/
private static void shareConfigurationObject(final Configuration configuration, final String sharedObjectFieldName) {
Throwable exception = null;
try {
// get the current object
Field sqlFragmentsField = Configuration.class.getDeclaredField(sharedObjectFieldName);
sqlFragmentsField.setAccessible(true);
Object currentObject = sqlFragmentsField.get(configuration);
// get the shared object field
Object sharedObject = sharedOrmConfigurationObjects.get(sharedObjectFieldName);
// replace current object with shared object if present
if (sharedObject == null) {
// first time => set the shared object
// Implementation note: since this is the first time, we don't need to update the current object
sharedOrmConfigurationObjects.put(sharedObjectFieldName, currentObject);
} else {
// we got a shared object => use it in the configuration
sqlFragmentsField.set(configuration, sharedObject);
}
} catch (NoSuchFieldException e) {
exception = e;
} catch (IllegalArgumentException e) {
exception = e;
} catch (IllegalAccessException e) {
exception = e;
}
if (exception != null) {
LOGGER.log(Level.WARNING, "could not configured ORM with shared " + sharedObjectFieldName + " field; " + exception.toString(), exception);
}
}
ProcessEngine
uses about 4 MB of memory and the next ProcessEngine
s use 0.4 MB, a 10x factor reduction. Configuration
fields do not change after using the ProcessEngine
, I used a test case which get each shared fields hashCode before and after using the ProcessEngine
.07-25-2014 12:04 PM
07-26-2014 03:33 AM
sharedOrmConfigurationObjects
, we could reference the first created ProcessEngine
's MyBatis configuration fields. But the ProcessEngines
keeps a Map<String,ProcessEngine>
as a HashMap
which is not ordered, so how could we get the first ProcessEngine
?
07-30-2014 03:32 AM
07-30-2014 08:44 PM
ProcessEngineConfigurationImpl
by adding in initSqlSessionFactory()
, after configuration.setEnvironment(environment)
, the following code:
Collection<ProcessEngine> processEngines = ProcessEngines.getProcessEngines().values();
if (!processEngines.isEmpty()) {
ProcessEngine firstProcessEngine = processEngines.iterator().next();
if (firstProcessEngine instanceof ProcessEngineImpl) {
ProcessEngineImpl pe = (ProcessEngineImpl)firstProcessEngine;
Configuration firstConfiguration = pe.getProcessEngineConfiguration().getSqlSessionFactory().getConfiguration();
shareConfigurationField(configuration, firstConfiguration, "sqlFragments");
shareConfigurationField(configuration, firstConfiguration, "mappedStatements");
shareConfigurationField(configuration, firstConfiguration, "resultMaps");
}
}
shareConfigurationField()
method:
/**
* Share a specific field of the MyBatis Configuration.
* @param configuration the current configuration
* @param sharedObjectFieldName the field to be shared
*/
private static void shareConfigurationObject(final Configuration configuration, final Configuration firstConfiguration, final String sharedObjectFieldName) {
Throwable exception = null;
try {
// get the field to share
Field sharedField = Configuration.class.getDeclaredField(sharedObjectFieldName);
sharedField.setAccessible(true);
Object sharedObject = sharedField.get(firstConfiguration);
// we got a shared object => use it in the configuration
sharedField.set(configuration, sharedObject);
} catch (NoSuchFieldException e) {
exception = e;
} catch (IllegalArgumentException e) {
exception = e;
} catch (IllegalAccessException e) {
exception = e;
}
if (exception != null) {
LOGGER.log(Level.WARNING, "could not configured ORM with shared " + sharedObjectFieldName + " field; " + exception.toString(), exception);
}
}
08-22-2014 03:10 AM
08-22-2014 06:40 AM
Tags
Find what you came for
We want to make your experience in Hyland Connect as valuable as possible, so we put together some helpful links.