How does the TestContext cache bound its size, and what happens when the limit is exceeded?
answer
- Default maxSize = 32
- LRU eviction, evicted context is closed
- spring.test.context.cache.maxSize
- -D system prop or spring.properties file
- Too many keys => thrashing = rebuild loop
basics
~20 sThe cache holds a bounded number of contexts (default 32) and evicts the least-recently-used one when full. You change the limit with the spring.test.context.cache.maxSize property, set as a JVM system property or in a spring.properties file.
solid answer
~40 sThe `ContextCache` is an LRU cache with a maximum size, default **32**, configured by `spring.test.context.cache.maxSize`. When a new context must be cached and the cache is already full, the framework evicts the **least-recently-used** context to make room; eviction closes that context (calls `close()`, releasing beans, pools, etc.). If your suite creates more than `maxSize` distinct contexts, you get **thrashing**: contexts that would have been reused are evicted and rebuilt repeatedly, which can make a suite dramatically slower — the classic symptom of too many distinct cache keys. You set the property either as a JVM system property (`-Dspring.test.context.cache.maxSize=64`) or via Spring's `SpringProperties` mechanism (a `spring.properties` file on the classpath). Raising it treats the symptom; the better fix is usually reducing the number of distinct contexts.
code
java · 10 lines// Gradle: raise the cache size for the test JVM
// build.gradle.kts
// tasks.test {
// systemProperty("spring.test.context.cache.maxSize", "64")
// }
// OR classpath: src/test/resources/spring.properties
// spring.test.context.cache.maxSize=64
// Prefer, though, to REDUCE distinct contexts so 32 is plenty.go deeper
Know there's a limit (32) and that old contexts get evicted when it's exceeded.
Explain LRU eviction, that eviction closes the context, and how to set spring.test.context.cache.maxSize.
Diagnose thrashing from cache stats and choose reducing distinct contexts over raising the limit, understanding the memory trade-off.
Own suite-wide context budget: cap distinct configurations by convention so the default 32 is never a constraint, and treat raising it as an escalation, not a default.
## Bounded, LRU The default `ContextCache` implementation (`DefaultContextCache`) is a **least-recently-used (LRU)** cache with a hard maximum count of cached contexts. The default maximum is **32**. Each time a context is accessed it becomes the most-recently-used; when a *new* context needs to be stored and the cache is at capacity, the **least-recently-used** entry is removed. ## Eviction closes the context Eviction isn't just dropping a reference — the framework **closes** the evicted `ApplicationContext`, invoking its shutdown (destroying singletons, closing connection pools, running `@PreDestroy`). So eviction has real cost, and a later test needing that configuration must **rebuild** it from scratch (a cache miss). ## Configuring the maximum The knob is the property `spring.test.context.cache.maxSize`. Two supported ways to set it: 1. **JVM system property:** `-Dspring.test.context.cache.maxSize=64` (e.g. in Gradle `test { systemProperty(...) }` or Surefire config). 2. **`SpringProperties`:** a `spring.properties` file at the root of the classpath containing `spring.test.context.cache.maxSize=64`. `org.springframework.core.SpringProperties` reads it. It must be a positive integer; a non-positive or unparseable value causes an error. ## Thrashing — the failure mode to recognize If a suite legitimately (or accidentally) needs **more distinct contexts than `maxSize`**, the cache constantly evicts and reloads. Because context startup is the expensive operation, thrashing can turn a fast suite into a very slow one, and it's non-obvious because everything still passes. Signs: - Cache logs (`org.springframework.test.context.cache` at DEBUG) show many misses and evictions and a size pinned at `maxSize`. - Suite runtime scales with number of test classes, not number of *configurations*. ## Two fixes, in priority order 1. **Reduce distinct contexts (preferred):** consolidate configurations, share mocks via base classes, use a shared static Testcontainer, avoid gratuitous `@TestPropertySource` variations. Fewer keys ⇒ everything fits and reuses. 2. **Raise `maxSize` (band-aid):** only if you genuinely have many *necessary* distinct configurations. More cached contexts means more memory held simultaneously (multiple connection pools, bean graphs), so raising it isn't free. ## Observability `org.springframework.test.context.cache.ContextCache` exposes hit/miss/size statistics that are logged; use them to decide between the two fixes rather than guessing.
- What's the downside of just cranking maxSize up to 200?Every cached context stays alive holding its beans, connection pools, and memory simultaneously, so you can exhaust memory or DB connections. It also masks the real problem — too many distinct configurations — rather than fixing it.
- How would you know the cache is thrashing rather than genuinely fast?Enable DEBUG logging on org.springframework.test.context.cache and watch the miss count and eviction count climb while size sits pinned at maxSize; suite time correlating with class count rather than config count is another sign.
saying these in an interview costs you the question
- Thinking the cache is unbounded
- Naming a wrong default (it's 32)
- Believing eviction just drops the reference without closing the context
- Reaching for maxSize increase before reducing the number of distinct contexts