JVM
This chapter discusses changes related to JVM.
Class-Data Sharing
Class-Data Sharing (CDS) is introduced in JDK 5. CDS allows a set of classes to be pre-processed into a shared archive file that can then be memory-mapped at runtime to reduce startup time. It can also reduce dynamic memory footprint when multiple JVMs share the same archive file.
In Java 12 (JEP 341), a CDS archive is generated automatically from the default class list. This archive file is put into the lib/server directory. Since the -Xshare:auto option was enabled by default for the server VM in Java 11, users can benefit from this CDS change automatically. If you don’t want this feature, it can be disabled via -Xshare:off.
In Java 10 (JEP 310), CDS was enhanced to allow application classes to be placed in the shared archive. CDS can be enabled for the system class loader, the platform class loader, and user-defined class loaders by specifying the -XX:+UseAppCDS option.
To actually use CDS for application classes, there are typically three steps:
First, we need to determine the classes to archive. This is done by running the application with -Xshare:off option, and -XX:DumpLoadedClassList option to record the classes that are loaded. For example:
1 $ java -Xshare:off -XX:+UseAppCDS \
2 -XX:DumpLoadedClassList=app.lst -cp app.jar MyApp
Then, we can create the AppCDS archive. This is done by using the -Xshare:dump -XX:+UseAppCDS options, with -XX:SharedClassListFile option to specify the list of classes generated in the step above. For example:
1 $ java -Xshare:dump -XX:+UseAppCDS \
2 -XX:SharedClassListFile=app.lst \
3 -XX:SharedArchiveFile=app.jsa -cp app.jar
Finally, we can use the AppCDS archive. This is done by using the -Xshare:on -XX:+UseAppCDS options. For example:
1 $ java -Xshare:on -XX:+UseAppCDS \
2 -XX:SharedArchiveFile=app.jsa \
3 -cp app.jar MyApp
Steps above make AppCDS inconvenient to use, because a trial run is required to generate the class list. In Java 13 (JEP 350), we can enable dynamic archiving to eliminate the first step.
The first step is to create a shared archive dynamically when an application exits. All we need to do is to specify the -XX:ArchiveClassesAtExit option.
1 $ java -XX:ArchiveClassesAtExit=app.jsa -cp app.jar MyApp
Then we can run the application again using the dynamic archive.
1 $ java -XX:SharedArchiveFile=app.jsa -cp app.jar MyApp
The dynamic archive requires the default CDS archive to be used as its base archive.
![]() |
If the dynamically generated archive doesn’t meet your requirement, you can still use the three steps above to generate an archive. |
Helpful NullPointExceptions
NullPointerException is very common in Java programs. However, the message of this exception is not very helpful to identify the actual cause, especially for long reference paths. The message is just null. We can only use the line number to locate the place that causes this exception.
For long reference paths, it’s hard to pinpoint the actual null object. For example, if a NullPointerException is thrown when an access path like a.b.c.d, it could be a, b, or c is null. It’s hard to tell by checking the line numbers.
In Java 14 (JEP 358), a new option -XX:+ShowCodeDetailsInExceptionMessages can be specified to enable detailed messages of NullPointerException. The message is generated by analyzing bytecode instructions by the JVM.
The code below throws a NullPointException when accessing b.a.
Without the detailed message, the exception message looks like below. We can only know that the exception happens at line 18.
1 Exception in thread "main" java.lang.NullPointerException
2 at io.vividcode.java11to17.jvm.NPE.main(NPE.java:18)
With the detailed messages enabled, the exception message looks like below. From the message, we can know that b.a is null.
1 Exception in thread "main" java.lang.NullPointerException: Cannot invoke "io.vividcode.java11to17.jvm.NPE$A.doSomething()" because "b.a" is null
2 at io.vividcode.java11to17.jvm.NPE.main(NPE.java:18)
As of Java 17, the option ShowCodeDetailsInExceptionMessages has been enabled by default. If you don’t like it, it can be disabled by specifying the -XX:-ShowCodeDetailsInExceptionMessages option.
Elastic Metaspace
In JVM, metaspace memory is managed in per-class-loader arenas. For applications that use many small class loaders, this may cause high metaspace usage. In Java 16 (JEP 387), the metaspace memory allocator has been replaced with a buddy-based allocation scheme, which can reduce class loader overhead.
The memory is committed from the operating system to arenas on demand, which can reduce footprint for certain class loaders.
The metaspace memory is arranged into uniformly-sized granules which can be committed and uncommitted independently of each other.
Deprecate and Disable Biased Locking
Biased locking is an optimization technique used in the HotSpot Virtual Machine to reduce the overhead of uncontended locking. Biased locking doesn’t bring much performance gains in nowadays applications. However, it makes code hard to maintain.
In Java 15 (JEP 374), biased locking is disabled by default. It can still be enabled using the -XX:+UseBiasedLocking option. The option UseBiasedLocking and all options related to the configuration and use of biased locking are deprecated.
