I have a somewhat complicated Spring Boot app, with a large number of tests.
When running the tests, it seems to be accumulating a lot of threads, one of which there is multiple instances of and is called SimplePauseDetectorThread_0
, which I traced down to this dependency
| | | \--- io.micrometer:micrometer-core:1.1.1
| | | +--- org.latencyutils:LatencyUtils:2.0.3
This seems to happen on Spring Boot 2.0.6 as well as 2.1.1.
A typical test may look like this:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@ActiveProfiles(profiles = {"test"})
public class MyTest {
[...]
My actuator config looks like this:
management.endpoints.enabled-by-default=false
management.endpoint.prometheus.enabled=true
management.endpoints.web.base-path=/
management.endpoints.web.exposure.include=prometheus
management.endpoints.web.path-mapping.prometheus=prometheus
spring.metrics.prometheus.enabled=true
See attached screenshot
Spring's integration testing support has the following primary goals: To manage Spring IoC container caching between test execution. To provide Dependency Injection of test fixture instances. To provide transaction management appropriate to integration testing.
The default value for the minimum heap is 8 Mb or 1/64th of the physical memory within the 8 Mb to 1 Gb range. The default value for the maximum heap is 1/4th of the physical memory for physical memory greater than 192 MB, otherwise, it's 1/2th of the physical memory.
Add an ORM and a few modules and it will quickly skyrocket to 7+ seconds. Add a bunch of profiles, and scatter them through a few tests and we'll quickly get a 60+ seconds build (assuming we run tests as part of our build – and we should).
Snicoll from Pivotal helped me on GitHub, by suggesting that it was probably connected to context caching in the spring boot test framework.
If you have a large number of tests that are using the spring integration and a somewhat important number of context configurations (you've only shown one class), then one context per configuration will be created and I can see the number of threads increasing in that scenario.
He then pointed me to the relevant documentation, which states:
You can configure the maximum size from the command line or a build script by setting a JVM system property named spring.test.context.cache.maxSize. As an alternative, you can set the same property programmatically by using the SpringProperties API.
And org.springframework.core.SpringProperties
states:
Reads a {@code spring.properties} file from the root of the Spring library classpath
Which leaves us with two ways of setting maxSize
.
Add a property to the gradle test
task which will configure the GradleWorkerMain
, in build.gradle
:
test {
jvmArgs "-Dspring.test.context.cache.maxSize=1"
}
If you have many subprojects you might want to use this option.
See Bonus below for a way to apply the setting to all your subprojects.
You can write the setting in my-service/src/test/resources/spring.properties
, like so:
spring.test.context.cache.maxSize=1
Now my tests are running nicely with less memory consumption, and fewer threads.
This also resolves the issue with Gradle 5+ having workers that have 512MB max heap by default (instead of 25% of system RAM) - the subproject test suites no longer blow away all the available RAM which would cause the workers to OOM if I did not add a custom jvmargs with a larger heap in the test
configuration of java
projects. I can now run with "vanilla" heap size in the gradle worker.
If one does want to tweak the RAM available to Gradle tests, do something like this in the root build.gradle
:
allprojects { project ->
project.plugins.withId('java') {
test {
maxHeapSize = "1536M"
// If you don't want to use spring.properties (or add other JVM args)
jvmArgs "-Dspring.test.context.cache.maxSize=1"
}
}
project.plugins.withId('java-library') {
test {
maxHeapSize = "1536M"
// If you don't want to use spring.properties (or add other JVM args)
jvmArgs "-Dspring.test.context.cache.maxSize=1"
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With