Implement AppCDS support for startup time improvement #5069
I have been playing with the new Application Class Data Sharing ("AppCDS") feature of Oracle's JDK, and the results have been good.
Combining AppCDS with our own flags (tier 1, no JRuby JIT) I get the following improvements:
This may see an even bigger improvement on platforms with notoriously bad startup time, like Linux.
Given these numbers, I think we should move forward with implementing AppCDS support in our launchers.
My current vision is as follows:
This would not be difficult to add to the bash script, and would only take a bit more work to add it to the native launcher.
I used the following instructions to generate an AppCDS cache specific to JRuby: https://github.com/kkinnear/zprint/blob/master/doc/filter.md#the-manual-way
Here's my session, showing all flags for dumping and using a CDS cache:
 ~/projects/jruby $ export GENERATE_CLASSLIST='-XX:+UnlockCommercialFeatures -XX:+UseAppCDS -Xshare:off -XX:DumpLoadedClassList=jruby.classlist'
 ~/projects/jruby $ export GENERATE_CDS='-XX:+UnlockCommercialFeatures -XX:+UseAppCDS -Xshare:dump -XX:SharedClassListFile=jruby.classlist -XX:SharedArchiveFile=jruby.cache'
 ~/projects/jruby $ export USE_CDS='-XX:+UnlockCommercialFeatures -XX:+UseAppCDS -Xshare:on -XX:SharedArchiveFile=jruby.cache'
 ~/projects/jruby $ time jruby --dev -e 1
 ~/projects/jruby $ export VERIFY_JRUBY=1 # move JRuby to normal classpath for CDS
 ~/projects/jruby $ JAVA_OPTS=$GENERATE_CLASSLIST jruby --dev -e 1
 ~/projects/jruby $ JAVA_OPTS=$GENERATE_CDS jruby --dev -e 1
Allocated shared space: 314572800 bytes at 0x0000000800000000
Loading classes to share ...
 ~/projects/jruby $ JAVA_OPTS=$USE_CDS time jruby --dev -e 1
1.18 real 1.67 user 0.18 sys
The text was updated successfully, but these errors were encountered:
We also should revisit "real" AOT for Ruby code, since there are now many technologies that could make precompiled bytecode load significantly faster than loading from source:
It's possible we could get to the point where the only Ruby code being parsed and compiled at boot time is the user's application, allowing the features above to boot up JRuby, stdlib, and gems quickly.
@kares I think he means saving java bytecode generated types for methods. Way back in the day this was attempted but it had two problems: verification overhead and permgen. I think even with verification disabled loading those classes still took a long time. we should not longer have the permgen issue either. If this is just one epic mmap this may be golden?
I think we are ok right now in that we only have conservative optimizations in IR so it should be safe to try. This will be a more complicated idea in the future as we need IR for profiled optimizations but then we will just dump that into another location so we can still load it. Pretty orthogonal.
Does JRuby use the Application class loader to load most of it’s classes or does it use custom class loaders? AppCDS currently only works for the Application class loader out of the box. For Tomcat for example it only can cache about 30% of the classe (see https://simonis.github.io/CDS/cds.xhtml how you can fix it - but it’s still work in progress :)
@simonis Most of the classes that will load in the first few seconds of execution will just come from the normal system classloaders. Methods that JIT at runtime are usually loaded into their own classloaders, one per method, to allow them to GC.
If we produced .class files AOT, however, we could load those with the system classloader and AppCDS would still work. There's not as much need to make code loaded from shared .class files be individually GCable since it's a bounded set.
This has been largely addressed by the jruby-startup gem, which combined with jruby.bash changes in JRuby 9.2.1 makes it possible to boost startup on Java 11+.
I will write up separate documentation, but see https://github.com/jruby/jruby-startup for now.