From 0cb22fc8f34a29c42313056671a947444b60050b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 18 Apr 2015 17:36:39 +0200 Subject: [PATCH] Use SoftReferences for context caching in the TCF Prior to this commit, the ContextCache in the Spring TestContext Framework (TCF) cached ApplicationContexts in a ConcurrentHashMap using strong references. This practice can occasionally lead to OutOfMemoryErrors when running a large number of tests in a test suite with varying context configuration since the context cache becomes overpopulated over time. This commit addresses this issue by using Spring's ConcurrentReferenceHashMap which uses SoftReferences for both the keys (i.e., MergedContextConfiguration instances) and values (i.e., ApplicationContexts) stored in the map that backs the ContextCache. Issue: SPR-7687 --- .../test/context/ContextCache.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java index f14e6560628..5f18399d80d 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java @@ -21,7 +21,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.context.ApplicationContext; @@ -29,14 +28,13 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.style.ToStringCreator; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; import org.springframework.util.Assert; +import org.springframework.util.ConcurrentReferenceHashMap; /** * Cache for Spring {@link ApplicationContext ApplicationContexts} in a test * environment. * - *

{@code ContextCache} maintains a cache of {@code ApplicationContexts} - * keyed by {@link MergedContextConfiguration} instances. - * + *

Rationale

*

Caching has significant performance benefits if initializing the context * takes a considerable about of time. Although initializing a Spring context * itself is very quick, some beans in a context, such as a @@ -44,9 +42,17 @@ import org.springframework.util.Assert; * time to initialize. Hence it often makes sense to perform that initialization * only once per test suite. * + *

Implementation Details

+ *

{@code ContextCache} maintains a cache of {@code ApplicationContexts} + * keyed by {@link MergedContextConfiguration} instances. Behind the scenes, + * Spring's {@link ConcurrentReferenceHashMap} is used to store + * {@linkplain java.lang.ref.SoftReference soft references} to cached contexts + * and {@code MergedContextConfiguration} instances. + * * @author Sam Brannen * @author Juergen Hoeller * @since 2.5 + * @see ConcurrentReferenceHashMap */ class ContextCache { @@ -54,7 +60,7 @@ class ContextCache { * Map of context keys to Spring {@code ApplicationContext} instances. */ private final Map contextMap = - new ConcurrentHashMap(64); + new ConcurrentReferenceHashMap(64); /** * Map of parent keys to sets of children keys, representing a top-down tree @@ -63,7 +69,7 @@ class ContextCache { * of other contexts. */ private final Map> hierarchyMap = - new ConcurrentHashMap>(64); + new ConcurrentReferenceHashMap>(64); private final AtomicInteger hitCount = new AtomicInteger();