Java Class Data Sharing or how to speed up your docker container startup
Since some Java versions you can use CDS (Class Data Sharing) feature. It enables to generate a file containing the result of standard classloading and preload it to gain some classloading time.
For application doing a lot of classloading at startup (it will be the case of most EE/CDI containers or any library doing classpath scanning with runtime validation), it can save some precious milliseconds to seconds.
On my CDI application which was preoptimized by not scanning anything the startup time went from 1.4s to <1s, it is almost 30% of gain!
The main drawbacks of that feature are that:
- The classpath must be stable (long story short you should try to keep the archive classpath match the actual runtime classpath start, i.e. you can append things but not change the order or preppend jars. You should also avoid wildcards - *.jar). This sounds natural since it enables to ensure the loaded class is the expected one, not respecting this "beginning of classpath" rule will make the runtime with an unexpected behavior.
- The archive is specific to a JVM version (but in a container it is the case anyway).
This last point means you must generate the archive with the JVM you have in the container and not with your host/dev one - or ensure it is the same.
Personally I do it with these steps:
- I create my image without any CDS setup (but i ensure the classpath is explicit and sorted or at least created in a repeatable order, which means if you want to use Jib you must do it programmatically and not through its gradle/maven plugins),
- I launch the docker image (ensuring the configuration will not await a server) mounting a local folder in /tmp/cds with the option -XX:DumpLoadedClassList=/tmp/cds/classes.lst,
- I relaunch the image with the same setup but new options: -Xshare:dump -XX:SharedClassListFile=/tmp/cds/classes.lst -XX:SharedArchiveFile=/tmp/cds/application.jsa
- I add the application.jsa file in a new image and add the option -XX:SharedArchiveFile=/path/to/application.jsa to the entry point
Now, since CDS i on by default since some java versions, you will skip most of the classloading time and make your service up way faster :).
Some tips I use around that feature:
- For CDI application, ensure to replace the scanning with an extension which registers beans (beforeBeanDiscovery..addAnnotatedType(var2.createAnnotatedType(MyBean.class)) in an extension can be combined with SeContainerInitializer disableDiscovery method for example),
- Ensure the generation 2. go through most of your required classloading (not listed classes will be ignored),
- For the classpath I started from maven dependency plugin to put the classpath of the module in a property (dependency:build-classpath) then read this property in a plain main() executed with exec-maven-plugin and using jib-core. Parsing the classpath enables to sort it deterministically and ensure the order of the generated archive will always be the same. This classpath is then used in jib entrypoint.
- You can append to the classpath a /foo/* to keep an extension folder.
I opened an issue on jib bugtracker because I think it is worth supporting OOTB, don't hesitate to vote for this issue: https://github.com/GoogleContainerTools/jib/issues/2471
From the same author:
In the same category: