Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 313482 - Memory leak when using XSD Metamodels in MWE Workflows
Summary: Memory leak when using XSD Metamodels in MWE Workflows
Status: RESOLVED FIXED
Alias: None
Product: EMFT
Classification: Modeling
Component: MWE (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: RC2   Edit
Assignee: Moritz Eysholdt CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 310359 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-05-19 04:49 EDT by Daniel Luebke CLA
Modified: 2010-05-20 08:48 EDT (History)
3 users (show)

See Also:
moritz.eysholdt: helios+


Attachments
Hack for working around the memory leak (3.91 KB, application/octet-stream)
2010-05-19 04:49 EDT, Daniel Luebke CLA
no flags Details
Fix-v1 (9.39 KB, patch)
2010-05-20 06:19 EDT, Moritz Eysholdt CLA
sven.efftinge: review+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Luebke CLA 2010-05-19 04:49:05 EDT
Build Identifier: M20090917-0800

If you run a MWE workflow multiple times from a Java program and that workflow uses a XSDMetaModel component, the XSD files will be loaded multiple times and stay in memory. If deployed on the server-side, this will lead to serious problems.
We diagnosed this problem with the SAP Memory Analyzer tool after we detected a memory leak in our generator.

In our project, we introduced a workaround by subclassing the XSDMetaModel and caching the dynamic ecore metamodel based on the XSD files. This is only a hack. A better solution IMO would be see whether the namespace is already registered. However, it works for our case. The class looks like this:

public class XsdCachedMetaModel extends XSDMetaModel {

	private static final Map<List<String>, XSDMetaModel> cache = new HashMap<List<String>, XSDMetaModel>(); 

	private static synchronized XSDMetaModel getMetaModel(List<String> uris) {
		if(!cache.containsKey(uris)) {
			XSDMetaModel xsdMM = new XSDMetaModel();
			
			for(String uri : uris) {
				xsdMM.addSchemaFile(uri);
			}
			
			cache.put(uris, xsdMM);
		}
		
		return cache.get(uris);
	}
	
	private List<String> uris = new ArrayList<String>();
	
	public void addSchemaFile(String uri) {
		uris.add(uri);
	}

	@Override
	public EPackage[] allPackages() {
		return getMetaModel().allPackages();
	}

	@Override
	public EFeatureMapEntryTypeImpl getEFeatureMapEntryType() {
		return getMetaModel().getEFeatureMapEntryType();
	}

	@Override
	public XMLFeatureMapTypeImpl getEFeatureMapType(EClass aClass) {
		return getMetaModel().getEFeatureMapType(aClass);
	}

	@Override
	public EFeatureType getEFeatureType() {
		return getMetaModel().getEFeatureType();
	}

	@Override
	public EMapEntryType getEMapEntryType(EClassifier innerType) {
		return getMetaModel().getEMapEntryType(innerType);
	}

	@Override
	public EMapType getEMapType(EClassifier innerType) {
		return getMetaModel().getEMapType(innerType);
	}

	@Override
	public Set<Type> getKnownTypes() {
		return getMetaModel().getKnownTypes();
	}

	private XSDMetaModel getMetaModel() {
		return getMetaModel(uris);
	}

	@Override
	public Set<String> getNamespaces() {
		return getMetaModel().getNamespaces();
	}

	@Override
	public QNameType getQNameType() {
		return getMetaModel().getQNameType();
	}

	@Override
	public Type getType(Object obj) {
		return getMetaModel().getType(obj);
	}

	@Override
	public Type getTypeForEClassifier(EClassifier element) {
		return getMetaModel().getTypeForEClassifier(element);
	}

	@Override
	public Type getTypeForETypedElement(ETypedElement typedElement) {
		return getMetaModel().getTypeForETypedElement(typedElement);
	}

	@Override
	public Type getTypeForName(String typeName) {
		return getMetaModel().getTypeForName(typeName);
	}

	@Override
	public TypeSystem getTypeSystem() {
		return getMetaModel().getTypeSystem();
	}
	
	@Override
	public XSDManager getXsdManager() {
		return getMetaModel().getXsdManager();
	}
	
	@Override
	public boolean isRegisterPackagesGlobally() {
		return getMetaModel().isRegisterPackagesGlobally();
	}

	@Override
	public boolean isSaveEPackages() {
		return getMetaModel().isSaveEPackages();
	}

	@Override
	public void setRegisterPackagesGlobally(boolean registerPackagesGlobally) {
		getMetaModel().setRegisterPackagesGlobally(registerPackagesGlobally);
	}

	@Override
	public void setSavePackagesPath(String path) {
		getMetaModel().setSavePackagesPath(path);
	}

	@Override
	public void setTypeSystem(TypeSystem typeSystem) {
		getMetaModel().setTypeSystem(typeSystem);
	}

	@Override
	public String toString() {
		return getMetaModel().toString();
	}

}


Reproducible: Always

Steps to Reproduce:
1. Create workflow using XSDMetaModel
2. Run this workflow in a loop from a WorkflowRunner and print memory consumption
3. See memory consumption increase and finally the program exit with an OutOfMemoryException.
Comment 1 Daniel Luebke CLA 2010-05-19 04:49:53 EDT
Created attachment 169068 [details]
Hack for working around the memory leak
Comment 2 Jochen Schmich CLA 2010-05-20 03:16:47 EDT
Please see Bug 310359 - Posted the same issue some weeks ago.
Comment 3 Sven Efftinge CLA 2010-05-20 03:31:52 EDT
*** Bug 310359 has been marked as a duplicate of this bug. ***
Comment 4 Moritz Eysholdt CLA 2010-05-20 05:07:12 EDT
Hi Daniel,

As pointed out in Bug 310359, there is XSDMetaModel.mmRegistry, which is an (ugly) static variable which keeps the loaded meta models for the JVM's lifetime. 

I introduced this registry to be able to access an XSDMetaModel from within Xpand/Xtend via XMLReaderHelper.readXML(String, String).

Sven, is there any way to access to current workflow context from within Xpand/Xtend? This seems to be a more appropriate context for this registry.

Daniel, please note that mmRegistry is only populated with XSDMetaModels that have the ID-attribute specified.
Comment 5 Sven Efftinge CLA 2010-05-20 05:10:01 EDT
The ExecutionContext is the place to store any Xpand-global state. 
I opened up (made mutable) the global vars map just yesterday :-)
Comment 6 Moritz Eysholdt CLA 2010-05-20 05:16:18 EDT
The state is actually Worflow-global and no just Xpand-gobal: The XSDMetaModel is typically created before Xpand/Xtend-code is executed and lives on for all further runs of Xpand/Xtend in the same workflow.
Comment 7 Sven Efftinge CLA 2010-05-20 05:24:33 EDT
in that case the meta model seems to be the right place. Just removing the static keyword wouldn't work?
Comment 8 Moritz Eysholdt CLA 2010-05-20 06:19:55 EDT
Created attachment 169304 [details]
Fix-v1

This fixes the problem with the static mmRegistry by removing the registry. The use case that the registry was needed for is now handled by taking the MetaModel from Xtend's execution context.

Please review the patch.
Comment 9 Moritz Eysholdt CLA 2010-05-20 08:48:05 EDT
Fixed in HEAD. Please verify if this solves the memory leaking issue.

The fix will be available with the next build from Hudson and the RC2 release.