Monday, March 8, 2010

Struts2 - StrutsSpringTestCase - Memory Leak

Edit 12/03/2010: This change has been committed to the Struts 2 code base (https://issues.apache.org/jira/browse/WW-3402) and should be available in one of the new versions. I was using version 2.1.8.


I recently ran into some problems while trying to implement JUnit tests for a Struts2 / Spring2.5 project I was doing. The problem was that when running the tests on some machines we were getting OutOfMemory exceptions. At first I thought this was a environment problem as on my machine it was working fine and I had the oldest and crappiest laptop out of everyone. However from what I could see everyone had the same settings and JVM, etc.

Next I fired up JVisualVM and was able to see everyone was having the same problem. The only difference was that my laptop, the only one running Linux, was handling the memory issue better.



Upon further investigation with JVisualVM I was able to see a bunch of instances for my Spring beans via the heap dump (in some cases 50+). This lead me to look further into the code for the org.apache.struts2.StrutsSpringTestCase class and saw that it was loading the applicationContext every single time and not checking whether it had already been set as was the case with my Spring test cases which extended org.springframework.test.AbstractDependencyInjectionSpringContextTests. And because it was static I guess the garbage collector was not removing them.

I am not sure if this behaviour was intended or not. But I overrode the setupBeforeInitDispatcher of the StrutsSpringTestCase with the following. The only difference being the check whether applicationContext is null.


protected void setupBeforeInitDispatcher() throws Exception {
// only load beans from spring once
if (applicationContext == null) {
GenericXmlContextLoader xmlContextLoader = new GenericXmlContextLoader();
applicationContext = xmlContextLoader.loadContext(getContextLocations());
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
}


After that the memory usage issues were resolved, the tests ran a hell of a lot quicker and I did not have a bunch of instances of my spring beans in the heap. I would recommend to anyone using the supplied StrutsSpringTestCase to do the same.



I am aware of other solutions via the Struts 2 documentation that have their own code for the testing of struts action but I prefer to stick with the classes supplied with the Struts 2 code.