Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Blocker
-
Resolution: Fixed
-
Affects Version/s: 1.1.2
-
Fix Version/s: 1.1.2
-
Component/s: Persistence
-
Labels:None
Description
The memory leak problem was detected after we patched the Grails 1.1.1 with http://jira.codehaus.org/browse/GRAILS-4580 changes http://github.com/grails/grails/commit/a7af74af3abb85faa75652433b3bb07e257aed43
But in fact the reason of memory leak is http://jira.codehaus.org/browse/GRAILS-3163 changes diff (added lines 283-290)
Just memory leak became more obvious with http://jira.codehaus.org/browse/GRAILS-4580 changes.
So about the problem:
With http://jira.codehaus.org/browse/GRAILS-4580 changes GroovyAwareJavassistLazyInitializer#getProxy(...) calls HibernatePluginSupport.initializeDomain(persistentClass);
And HibernatePluginSupport#initializeDomain(persistentClass) calls lazyInit closure on GrailsDomainClass which calls registerDynamicMethods(...)
HibernatePluginSupport#registerDynamicMethods(...) where addValidationMethods(...) is called where executed the code introduced with http://jira.codehaus.org/browse/GRAILS-3163 changes
...
MetaProperty originalPropertiesProperty = metaClass.getMetaProperty("properties")
metaClass.setProperties = {Object o ->
originalPropertiesProperty.setProperty delegate, o
if(delegate.hasErrors()) {
GrailsHibernateUtil.setObjectToReadyOnly delegate,sessionFactory
}
}
...
Let look at the code above more carefully:
The originalPropertiesProperty is the MetaBeanProperty for "properties" with getter and setter.
The metaClass.setProperties =
But in fact metaClass.setProperties = {Object o -> ...}
also creates new instance of MetaBeanProperty for "properties" with old getter and new setter according to specified closure.
From other side specified closure uses originalPropertiesProperty inside and it leads to memory leak because this created instance of MetaBeanProperty refers to previous MetaBeanProperty
It prevents these instances and all referenced instances to be collected by GC
In case of loading a lot of object with a lot of hibernate proxies it lead to OutOfMemoryError very soon even with big size of allocated heap memory.
It seems this memory leak was fixed by http://jira.codehaus.org/browse/GRAILS-4843 changes
- http://github.com/grails/grails/commit/3df4414877e7859aa269db22ca23e76b4a8d1140
- http://github.com/grails/grails/commit/40d14c402b44bb52f63ef9286079cc7c376fcd4f
in head/master but not in 1.1.x branch.
How to solve this problem for Grails 1.1.2 ?
Are http://jira.codehaus.org/browse/GRAILS-3163 changes related to metaClass.setProperties =
really necessary yet in Grails 1.1.x branch?
Can we just remove
...
MetaProperty originalPropertiesProperty = metaClass.getMetaProperty("properties")
metaClass.setProperties = {Object o ->
originalPropertiesProperty.setProperty delegate, o
if(delegate.hasErrors()) {
GrailsHibernateUtil.setObjectToReadyOnly delegate,sessionFactory
}
}
...
as it was done by http://github.com/grails/grails/commit/3df4414877e7859aa269db22ca23e76b4a8d1140 ?
Attached memoryLeak.PNG
shows that objects are not collected by CG after task was done completely.
shows why objects are not collected by CG.
can be useful to understand what happens on
Attached memoryLeakReason.PNG
Attached stackTrace.txt
metaClass.setProperties = {Object o -> originalPropertiesProperty.setProperty delegate, o if(delegate.hasErrors()) { GrailsHibernateUtil.setObjectToReadyOnly delegate,sessionFactory } }execution