You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When running on Gradle with a 256mb heap, this test performs ~200k executions before an OutOfMemoryError. The memory grab appears to be heavy-weight test results before retained in-memory. This was run against v5.7.0.
Create a heap dump, e.g. jmap -dump:live,format=b,file=/Users/ben/Downloads/junit.hprof "$(pgrep -f GradleWorkerMain)"
Open in your favorite profiler, e.g. JMC, YourKit, JProfiler (all free for OSS projects)
Context
I discovered this out of curiosity when facing a similar problem in TestNG (testng-team/testng#2096). It too retains the test results in-memory, though its stress tester runs faster and gets to ~600k before failing. In both cases the results shouldn't be held in-memory, e.g. perhaps streaming the results to disk if retained for a report.
The real-world case is Caffeine, which has a test suite that executes 4M+ scenarios. This is because a cache has many configuration options that interact, so a simple change could break a subsets of configurations for a given test case. Therefore the easiest solution is brute force testing with a custom @CacheSpec applied to the method to declare the specification constraints. The provider inspects the test method to generates all possible combinations.
Because of the memory grab, Caffeine's test suite is broken into ~30 tasks each running ~140k test executions. Over time I've whittled down the source problems by hacking the testing framework's internal structures to drop allocations. Prior tricks were modifying the test results in a listener, e.g. to replace parameters with their stringified version and dedupe test names. As more tests are added it keeps becoming a problem, requiring more effort to keep the build healthy. Recently I hacked the internals to drop the test result objects themselves (as not needed for Gradle's report). This shrank the heap from 315MB (8.6M objects) down to 22MB (0.5M objects), but is extremely hacky by reflectively clearing various collections. It functions enough for my build to shave the CI time by 20 minutes (22% speedup) which only offers a 512mb heap.
In both frameworks resolving this stress case might be an invasive change. While most test suites are small, better GC behavior can improve performance and lets the framework scale to more extreme scenarios.
The text was updated successfully, but these errors were encountered:
Steps to reproduce
When running on Gradle with a 256mb heap, this test performs ~200k executions before an OutOfMemoryError. The memory grab appears to be heavy-weight test results before retained in-memory. This was run against v5.7.0.
gradlew cleanTest test
until it slows downjmap -dump:live,format=b,file=/Users/ben/Downloads/junit.hprof "$(pgrep -f GradleWorkerMain)"
Context
I discovered this out of curiosity when facing a similar problem in TestNG (testng-team/testng#2096). It too retains the test results in-memory, though its stress tester runs faster and gets to ~600k before failing. In both cases the results shouldn't be held in-memory, e.g. perhaps streaming the results to disk if retained for a report.
The real-world case is Caffeine, which has a test suite that executes 4M+ scenarios. This is because a cache has many configuration options that interact, so a simple change could break a subsets of configurations for a given test case. Therefore the easiest solution is brute force testing with a custom
@CacheSpec
applied to the method to declare the specification constraints. The provider inspects the test method to generates all possible combinations.Because of the memory grab, Caffeine's test suite is broken into ~30 tasks each running ~140k test executions. Over time I've whittled down the source problems by hacking the testing framework's internal structures to drop allocations. Prior tricks were modifying the test results in a listener, e.g. to replace parameters with their stringified version and dedupe test names. As more tests are added it keeps becoming a problem, requiring more effort to keep the build healthy. Recently I hacked the internals to drop the test result objects themselves (as not needed for Gradle's report). This shrank the heap from 315MB (8.6M objects) down to 22MB (0.5M objects), but is extremely hacky by reflectively clearing various collections. It functions enough for my build to shave the CI time by 20 minutes (22% speedup) which only offers a 512mb heap.
In both frameworks resolving this stress case might be an invasive change. While most test suites are small, better GC behavior can improve performance and lets the framework scale to more extreme scenarios.
The text was updated successfully, but these errors were encountered: