Class Data Sharing

Sharing Economy in the HotSpot VM

Volker Simonis, SAP / volker.simonis@gmail.com

https://simonis.github.io/FOSDEM2018

Class Data Sharing

  • Cache preprocessed class metadata on disc
  • Improve startup performance
  • Reduce memory footprint

Short History of CDS in the HotSpot VM

  • Introduced 2004 with JDK 1.5
    • Only for Client VM, only Serial GC
    • Only system classes
  • Part of the initial OpenJDK 6/7 contribution
  • Since OpenJDK 9 / JDK 9:
    • Support for Server VM
    • Support for G1, Serial, Parallel and ParallelOld GCs
    • Support for shared strings (only with G1, non-Windows)
    • Support for application classes (commercial Oracle JDK feature)
  • Coming with OpenJDK 10:

Class representation in the HotSpot VM

 
 
 
 
 
 
 
 
 
 

CDS Basics - Create the Archive

              
$ java -Xshare:dump
Allocated shared space: 3221225472 bytes at 0x0000000800000000
Loading classes to share ... // from JAVA_HOME/lib/classlist
Rewriting and linking classes ...  // Verification, ...
Number of classes 1240
Removing unshareable information ... done.
Relocating embedded pointers ...
Dumping symbol table ...                                
Dumping String objects to closed archive heap region ...
Removing java_mirror ... done.  // java.lang.Class<?>
mc  space:     21832 [  0,1% ] ... at 0x0000000800000000
rw  space:   4093816 [ 22,2% ] ... at 0x0000000800006000
ro  space:   7313320 [ 39,7% ] ... at 0x00000008003ee000
md  space:      6352 [  0,0% ] ... at 0x0000000800ae8000
od  space:   6462536 [ 35,1% ] ... at 0x0000000800aea000
st0 space:    458752 [  2,5% ] ... at 0x00000000fff00000
oa0 space:     65536 [  0,4% ] ... at 0x00000000ffe00000
total    :  18422144 [100.0% ]  // JAVA_HOME/lib/server/classes.jsa
              
            

CDS Basics - Using the Archive

              
package io.simonis;

public class HelloCDS {
  public static void main(String[] args) throws IOException {
    System.out.println("Hello CDS");
    System.in.read();
  }
}
              
            

CDS Basics - Using the Archive

              
$ java -Xshare:on io.simonis.HelloCDS
HelloCDS
$ java -Xshare:on -Xlog:class+load io.simonis.HelloCDS
[class,load] java.lang.Object source: shared objects file
...
[class,load] j.i.l.URLClassPath$FileLoader source: jrt:/java.base
...
[class,load] io.simonis.HelloCDS2 source: file:/examples/bin/
HelloCDS
$ java -Xshare:on -Xlog:class+load io.simonis.HelloCDS \
      | grep "shared objects file" | wc
    477    2862   40977
$ java -Xshare:on -Xlog:class+load io.simonis.HelloCDS \
      | grep -v "shared objects file" | wc
      5      17     424
              
            

CDS Basics - Performance

              
$ time -f "%e sec\n" java -Xshare:off io.simonis.HelloCDS
Hello CDS
0.162 sec
$ time -f "%e sec\n" java -Xshare:on  io.simonis.HelloCDS          
Hello CDS
0.148 sec
              
            

About 9% overall performance improvement

              
$ java -Xshare:off -Xlog:class+load io.simonis.HelloCDS
[0,164s][class,load] io.simonis.HelloCDS source: file:/examples/bin
$ java -Xshare:on  -Xlog:class+load io.simonis.HelloCDS
[0,143s][class,load] io.simonis.HelloCDS source: file:/examples/bin
              
            

Application class loads about 13% faster

CDS Basics - Memory

              
$ java -Xshare:on io.simonis.HelloCDS &
HelloCDS
$ pmap -X `pgrep -f HelloCDS`
5707:  /jdk/bin/java -Xshare:auto io.simonis.HelloCDS
       Address Perm    Size   Rss   Pss Mapping
      00400000 r-xp       8     8     8 /jdk/bin/java
      00601000 r--p       4     4     4 /jdk/bin/java
      00602000 rw-p       4     4     4 /jdk/bin/java
      009b8000 rw-p     132    28    28 [heap]
      83200000 rw-p  129024  2048  2048 //   mapped Java heap
      8b000000 ---p 1914880     0     0 // unmapped Java heap
      ffe00000 rw-p      64    64    64 /jdk/lib/server/classes.jsa
      fff00000 rw-p     448   448   448 /jdk/lib/server/classes.jsa
     800000000 rwxp      24    24    24 /jdk/lib/server/classes.jsa
     800006000 rw-p    4000  3168  3168 /jdk/lib/server/classes.jsa
     8003ee000 r--p    7144  5160  5160 /jdk/lib/server/classes.jsa
     800ae8000 rw-p       8     8     8 /jdk/lib/server/classes.jsa
     800aea000 r--p    6312     0     0 /jdk/lib/server/classes.jsa
              
            

CDS & AppCDS with Tomcat 9 & nGrinder

              
$ CATALINA_OPTS="-Xshare:on \
                 -Xlog:class+load:file=/tmp/tomcat.classtrace" \
  catalina.sh start
$ wc /tmp/tomcat.classtrace
  12644   50575 2075264 /tmp/tomcat.classtrace
$ grep "shared objects file" /tmp/tomcat.classtrace | wc
   1156    6936  103052
$ CATALINA_OPTS="-XX:DumpLoadedClassList=/tmp/tomcat.cls" \
  catalina.sh start
$ wc /tmp/tomcat.cls
   3261   3261 125647 /tmp/tomcat.cls
$ CATALINA_OPTS="-XX:DumpLoadedClassList=/tmp/tomcat_appcds.cls \
                 -XX:+UseAppCDS" \
  catalina.sh start
$ wc /tmp/tomcat_appcds.cls
   3520   3520 133431 /tmp/tomcat_appcds.cls
              
            

CDS & AppCDS with Tomcat 9 & nGrinder

              
$ CATALINA_OPTS="-Xshare:dump -XX:+UseAppCDS \
                 -XX:SharedClassListFile=/tmp/tomcat_appcds.cls \
                 -XX:+UnlockDiagnosticVMOptions \
                 -XX:SharedArchiveFile=/tmp/tomcat_appcds.jsa" \
  catalina.sh start
$ ll /tmp/tomcat_appcds.jsa
-r--r----- 49.979.392 Feb  2 23:57 /tmp/tomcat_appcds.jsa
$ CATALINA_OPTS="-Xshare:on -XX:+UseAppCDS \
                 -XX:+UnlockDiagnosticVMOptions \
                 -XX:SharedArchiveFile=/tmp/tomcat_appcds.jsa" \
  catalina.sh start
              
            

AppCDS - Application Class Data Sharing

  • OpenJDK 10: JEP 310 - Application Class-Data Sharing
  • Supports sharing of classes loaded by the application class loader
    (a.k.a. system class loader) with -XX:+UseAppCDS
  • Supports sharing of classes loaded by custom class loaders
  • Unfortunately -XX:DumpLoadedClassList=.. -XX:+UseAppCDS
    doesn't dump classes loaded by custom class loaders

AppCDS - Class List Format

  • -XX:DumpLoadedClassList=... creates a trivial class list:
                      
    $ cat /tmp/tomcat_appcds.cls
    java/lang/Object
    java/lang/String                                          
    ...
    org/apache/catalina/startup/Bootstrap
                      
                    
  • The format for classes loaded by custom class loaders isn't specified anywhere, except: classListParser.cpp
                      
    CLASSNAME [ ID ]?
    CLASSNAME ID super: ID [ interface: [ ID ]+ ]? source: URL
                      
                    

Creating a Class List for Custom Loaders

  • -Xlog:class+load=debug has all we need:
                      
    $ CATALINA_OPTS="-Xlog:class+load=debug" catalina.sh start
    [..] java.lang.Object source: jrt:/java.base
    [..]  klass: 0x100000eb0 super: 0x000000000 loader: [NULL class loader]
    ....
    [..] java.sql.Timestamp source: jrt:/java.sql
    [..]  klass: 0x100096c30 super: 0x100091a00 \
          loader: [0xff0172600 a 'ClassLoaders$PlatformClassLoader']
    ....
    [..] org.apache.catalina.Server source: file:catalina.jar
    [..]  klass: 0x1000e6410 super: 0x100000eb0 interfaces: 0x1000e6238 \
          loader: [0xff0310fb0 a 'java/net/URLClassLoader']
                      
                    
  • From this we can easily create:
                      
    java/lang/Object           id: 0x100000eb0
    java/sql/Timestamp         id: 0x100096c30
    org/apache/catalina/Server id: 0x1000e6410 super: 0x100000eb0 \
                               interfaces: 0x1000e6238 source: catalina.jar
                      
                    

cl4cds

cl4cds converts a class log to a CDS class list:
                  
$ CATALINA_OPTS="-Xlog:class+load=debug:file=tomcat_dbg.clstrace" \
  catalina.sh start
$ java io.simonis.cl4cds tomcat_dbg.clstrace tomcat_cl4cds.cls
$ CATALINA_OPTS="-Xshare:dump -XX:+UseAppCDS \
                 -XX:SharedClassListFile=tomcat_cl4cds.cls \
                 -XX:+UnlockDiagnosticVMOptions \
                 -XX:SharedArchiveFile=tomcat_cl4cds.jsa" \
  catalina.sh start
$ ll tomcat_cl4cds.jsa
-r--r----- 1 102.305.792 Feb  3 01:48 tomcat_cl4cds.jsa
$ CATALINA_OPTS="-Xshare:on -XX:+UseAppCDS \
                 -XX:+UnlockDiagnosticVMOptions \
                 -XX:SharedArchiveFile=tomcat_cl4cds.jsa \
                 -Xlog:class+load:file=tomcat_cl4cds.clstrace" \
  catalina.sh start
$ wc tomcat_cl4cds.clstrace
  12644   69335 1531203 tomcat_cl4cds.clstrace
$ grep "shared objects " tomcat_cl4cds.clstrace | wc
   9380   56280  963418
                  
                

AppCDS - Current Limitations

  • Can't share pre 1.5 classes
  • Can't share dynamically generated classes
  • Can't share classes loaded from files (only .jar archives)
  • Can't share classes modified by the class loader
  • Can share only one class if loaded by several class loaders
  • No bytecode-rewriting (slow interpreter)

https://simonis.github.io/FOSDEM2018