We recently added some new integration tests to our test suite. All three tests were Spring integration tests. Normally in a Spring integration test, the entire code for a test method operates in a single transaction, and at the end of that test method, all database changes are rolled back, so your Spring context is safe to be cached and reused for the next test. However sometimes you are invoking code that starts and commits transactions, meaning data will be still in the database after the test has finished. To help deal with this scenario, Spring provides the @DirtiesContext annotation, which tells Spring to remove the context from its cache so a new context will be created for the next test. All three new tests used the @DirtiesContext annotation. However after they were checked in, the build failed with an out of memory error. We configured Java to generate a heapdump on out of memory and I looked at the heapdump with the Eclipse Memory Analyser Tool (MAT). This is what the results showed:
Yikes! There were eight separate instances of the Hibernate SessionFactoryImpl! It transpires that TestNG has a known bug in that it keeps references to your tests even after each test is completed. This means that even though Spring was removing the context from its cache and a new context was being created, most of the old context was not eligible for garbage collection.
My colleague Elko actually wrote an annotation for us that we use in our test suite, which clears the entire database after a test method. Not only does this avoid the TestNG memory leak, but is it massively faster, as it avoids reconstructing the test context, which takes around 30-45 seconds to construct. It is a TestNG listener which does the following:
- Turns off referential integrity
- Queries the information_schema table to get all table names
- Iterates over all tables and truncates each one
- Turns referential integrity back on