I am running tests that start multiple JVM processes. Summary startup time of JVMs is quite significant compared to time of actual tests that run inside JVM. How do I speed
To add to what has been said by others, next version of Java (Java 7 Java 8 Java 9) should add a new improvement in JVM start-up times, due to modularization of the platform, according to this link.
Quote:
One benefit of modularization is that the platform is a smaller download, potentially improving start-up performance. Having a smaller memory footprint also enables significant performance improvements, especially for desktop applications.
Run the tests with Java 5 or better. Startup time has been reduced significantly for Java 5.
You can't get much faster unless you can reuse a running VM: Prestarting a lot of them and then running a single test in each won't make your tests any faster.
So you need to figure a way to run several tests in a single VM.
JVM startup performance can be improved in many ways: CDS, straight up tuning, CPU pinning and jlink (since JDK 9) can be good options. AOT (JDK 9, Linux-only) has some rough edges but can be tuned to help small applications.
Class Data Sharing was developed for the client VM back in 1.5, but has been much improved since to work on all variants of HotSpot (all GCs since 8), bringing a substantial boost to startup performance when enabled.
CDS is always enabled on 32-bit JRE client installs, but might need some manual steps to be enabled elsewhere:
java -Xshare:dump
to generate a CDS shared archive-Xshare:auto
to your command to ensure it's usedWhile -client
may or may not actually do anything (the JVM on many systems doesn't ship with a client VM - none since 9, actually), there are numerous ways to tune a HotSpot server JVM to behave more like a client VM:
-XX:TieredStopAtLevel=1
-XX:CICompilerCount=1
-XX:+UseSerialGC
-Xmx512m
This should be enough to boost startup for a small short-running application, but may have very negative effects on peak performance. You can of course get even further by disabling features you might not be using, such as -XX:-UsePerfData
(disables some runtime information retrievable using MXBeans and jvmstat).
jlink is a new tool available in Java 9 which allows you to build custom runtime images. If your console application only uses a small subset of JDK modules, a custom runtime can be made very small, which can improve startup times further. A minimal image including only the java.base module, and might boost startup times by ~10-20ms depending on hardware and other tuning: $JAVA_HOME/bin/jlink --add-modules java.base --module-path $JAVA_HOME/jmods --output jbase
(Linux only) Java 9 introduces an experimental AOT compiler, jaotc
, which can be used to help applications boot faster and spend a lot less cycles doing so. Out-of-the-box it might slow down immediate startup (since the AOT'd code is a shared library which adds its own I/O overheads, doesn't support the Serial GC..), but with careful tuning we've seen it reduce start-up times of small applications by 15-30%.
CPU pinning: on large systems, we've seen effects that stem from cache coherence traffic between sockets, and binding to a single CPU node can cut startup times considerably. On Linux something like numactl --cpunodebind=0 $JAVA_HOME/bin/java ...
should do the trick.
All in all we've been able to get minimal applications to execute in as little as 35ms (JDK 9 GA). Various startup optimizations has gone into the JDK10 branch and I'm now seeing numbers as low as 28ms.
If it's really necessary for you to start a separate VM for each test, your best option is probably to make sure that you use one of the newest VM releases from Sun. The startup time has decreased quite a bit over the last Java versions and with 1.6.0_16, a simple "Hello world" program takes about 0.2s to run on my system.
If your question rather was ment to be "how to run more tests in one VM", the easiest approach depends on which test framework you are using (if you're not using a framework, you should really consider so). With JUnit and possibly the ant target for JUnit, you can either use patterns to match the tests you want to run or alternatively join different test classes in a test suite, which can then be run in one VM.
If you do want to reuse JVMs, the "somehow" could be Nailgun. Nailgun keeps a JVM running, then uses a light native client to start a particular class and handle console io. This is good for running small command line Java utilities, but as it reuses the same JVM can accumulate state.
To work around the state accumulation, at the cost of running multiple JVMs, another option is Drip. Drip keeps a fresh JVM spun up in reserve with the correct classpath and other JVM options so you can quickly connect and use it when needed, then throw it away. Drip hashes the JVM options and stores information about how to connect to the JVM in a directory with the hash value as its name.
Why not load all the tests in one JVM ? Can you do one or more of the following ?
Class.forName()
?If memory allocation is sizeable, it may speed things up by specifying the JVM memory startup size to the same figure as the maximum allocatable memory (-Xms
vs -Xmx
), which will save the JVM having to go back to the OS for more memory. However in this scenario I think that's unlikely to be the problem.