Skip to content

Commit 0cb22fc

Browse files
committed
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
1 parent 2a4f4cd commit 0cb22fc

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

spring-test/src/main/java/org/springframework/test/context/ContextCache.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,46 @@
2121
import java.util.List;
2222
import java.util.Map;
2323
import java.util.Set;
24-
import java.util.concurrent.ConcurrentHashMap;
2524
import java.util.concurrent.atomic.AtomicInteger;
2625

2726
import org.springframework.context.ApplicationContext;
2827
import org.springframework.context.ConfigurableApplicationContext;
2928
import org.springframework.core.style.ToStringCreator;
3029
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
3130
import org.springframework.util.Assert;
31+
import org.springframework.util.ConcurrentReferenceHashMap;
3232

3333
/**
3434
* Cache for Spring {@link ApplicationContext ApplicationContexts} in a test
3535
* environment.
3636
*
37-
* <p>{@code ContextCache} maintains a cache of {@code ApplicationContexts}
38-
* keyed by {@link MergedContextConfiguration} instances.
39-
*
37+
* <h3>Rationale</h3>
4038
* <p>Caching has significant performance benefits if initializing the context
4139
* takes a considerable about of time. Although initializing a Spring context
4240
* itself is very quick, some beans in a context, such as a
4341
* {@code LocalSessionFactoryBean} for working with Hibernate, may take some
4442
* time to initialize. Hence it often makes sense to perform that initialization
4543
* only once per test suite.
4644
*
45+
* <h3>Implementation Details</h3>
46+
* <p>{@code ContextCache} maintains a cache of {@code ApplicationContexts}
47+
* keyed by {@link MergedContextConfiguration} instances. Behind the scenes,
48+
* Spring's {@link ConcurrentReferenceHashMap} is used to store
49+
* {@linkplain java.lang.ref.SoftReference soft references} to cached contexts
50+
* and {@code MergedContextConfiguration} instances.
51+
*
4752
* @author Sam Brannen
4853
* @author Juergen Hoeller
4954
* @since 2.5
55+
* @see ConcurrentReferenceHashMap
5056
*/
5157
class ContextCache {
5258

5359
/**
5460
* Map of context keys to Spring {@code ApplicationContext} instances.
5561
*/
5662
private final Map<MergedContextConfiguration, ApplicationContext> contextMap =
57-
new ConcurrentHashMap<MergedContextConfiguration, ApplicationContext>(64);
63+
new ConcurrentReferenceHashMap<MergedContextConfiguration, ApplicationContext>(64);
5864

5965
/**
6066
* Map of parent keys to sets of children keys, representing a top-down <em>tree</em>
@@ -63,7 +69,7 @@ class ContextCache {
6369
* of other contexts.
6470
*/
6571
private final Map<MergedContextConfiguration, Set<MergedContextConfiguration>> hierarchyMap =
66-
new ConcurrentHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(64);
72+
new ConcurrentReferenceHashMap<MergedContextConfiguration, Set<MergedContextConfiguration>>(64);
6773

6874
private final AtomicInteger hitCount = new AtomicInteger();
6975

0 commit comments

Comments
 (0)