1. Foreword
In this article, you will learn about some system knowledge in OpenJDK HotSpot Java Virtual Machine (HotSpot JVM), and how to adjust them to get the best fit for your program and operating environment.
HotSpot JVM is an amazing and flexible technology. As a binary version, it is suitable for every major operating system and CPU architecture, from the miniature Raspberry Pi Zero to the "large" server with hundreds of CPU cores and terabytes of RAM. Since OpenJDK is an open source project, HotSpot JVM can be compiled for almost any other system, and it can be fine-tuned using options, switches, and flags.
First, here is some background. The language of HotSpot JVM is bytecode. At the time of writing, there are more than 30 programming languages that can be compiled into HotSpot JVM-compatible bytecode, but the most popular by far, with more than 8 million developers worldwide, is of course Java.
Java source code is compiled into bytecode ( as shown in Figure 1 ), compiled in the form of a class file, using 4 span3 4span3 44 In modern development, this may be abstracted away by build tools such as Maven , Gradle or IDE-based compilers.
Figure 1. The process of compiling the bytecode
The bytecode of the program indicates that it is executed by the HotSpot JVM on a virtual stack machine, which knows up to 256 different instructions , Each instruction is identified by an 8-digit opcode ; therefore, the name is "bytecode".
bytecode program is executed by the interpreter, the interpreter gets each instruction, pushes its operands onto the stack, then executes the instruction, removes the operands and leaves the result in the stack, such as Figure 2 as shown.
Figure 2. The result on the stack after the interpreter executes the bytecode
abstracts program execution from the underlying environment, giving Java the ability to "write once, run anywhere" Portability advantage. Class files compiled on one architecture can be executed on HotSpot JVM of a completely different architecture.
If you think that this abstraction of the underlying hardware comes at the expense of performance, then you are right. This is usually where the HotSpot JVM switches, options, and flags come in.
2. JIT just-in-time compilation
How can programs written in portable and feature-rich high-level languages (such as Java) challenge those who compile from "low-level" and "unfriendly" programming languages (such as C) to What about the performance of architecture-specific native code programs?
The answer is that HotSpot JVM includes a performance-enhancing just-in-time (JIT) compilation technology,It can analyze the execution of the program and selectively optimize the parts that it considers the most beneficial. These are called program hotspots (hence the name HotSpot JVM), which achieves this by dynamically compiling them into local code using knowledge of the underlying system architecture.
HotSpot JVM contains two JIT compilers, called C1 (client compiler) and C2 (server compiler), which provide different optimization trade-offs.
- C1 provides fast and simple optimization.
- C2 provides advanced optimization that requires more analysis, and the application cost is higher.
Since the release of JDK 8, the default behavior has been to use these two compilers at the same time in a mode called hierarchical compilation, where C1 provides a fast speed increase, and C2 collects before advanced optimization Sufficient evaluation information. The generated native code is stored in the memory area of the hotspot JVM , called the code cache, as shown in Figure 3 .
Figure 3. Java compilation process
3. GC garbage collection
In addition to JIT technology, HotSpot JVM also includes features such as increased productivity and performance, such as: Automatic memory management and selection of garbage collection (GC) strategies.
object is allocated in a memory area called heap of HotSpot JVM,Once these objects are no longer referenced, the garbage collector can clean them up and reclaim the memory used by them.
Fourth, the ergonomic HotSpot JVM
HotSpot JVM has so much flexibility and dynamic behavior, you may worry about how to configure it to best meet your program requirements. Fortunately, for many use cases, you don't need to make any manual adjustments. The HotSpot JVM includes a process called ergonomic (ergonomics), which checks the execution environment at startup and selects some reasonable default values for the GC strategy, heap size, and JIT compiler based on the number of CPU cores and the number of available RAM. The current default value is:
- Garbage Collector: G1 GC
- Initial heap: 1/64 of physical memory
- pan3 li93span
- span li93 1/4 of physical memory : Use the layered compilation of C1 and C2 at the same time
By using the option -XX:+PrintFlagsFinal _span4emspan _span4emspan 168 _span4emspan _span4emspan _span4emspan _span4emspan _span4emspan _span4emspan 168 Ergonomic default values for all your environment selected as follows:
java -XX: + PrintFlagsFinal | grep ergonomic intx CICompilerCount = 4 {product} {ergonomic} uint ConcGCThreads = 2 {product} {ergonomic} uint G1ConcRefinementThreads = 8 {product} {ergonomic} size_t G1HeapRegionSize = 2097152 {product} {ergonomic} {product} {ergonomic} size_t G1HeapRegionSize = 2097152 {product} {ergonomic} ergonomics 152 {product} {ergonomic} size{product} 152Intx GCDrainStackTargetSize {product} 152 {product} size{product} {product}}}} ergonomics 152Int x GCDrainStackTarget size {product} 152 {product }} oduct} {ergonomic} size_t MarkStackSize = 4194304 {product} {ergonomic} size_t MaxHeapSize = 8403288064 {product} {ergonomic} size_t MaxNewSize = 5041553408 {product} {ergonomic} size_t MinHeapDeltaBytes = 2097152 {product} {ergonomic} uintx NonNMethodCodeHeapSize = 5836300 { pd product} {ergonomic} uintx NonProfiledCodeHeapSize = 122910970 {pd product} {ergonomic} uintx ProfiledCodeHeapSize = 122910970 {pd product} {ergonomic} uintx ReservedCodeCacheSize = 251658240 {pd product} {ergonomic} bool SegmentedCodeCache = true {product} {ergonomic} bool UseCompressedClassPointers = true {ergonomic} bool UseCompressedOops = true {ergonomic} bool UseG1GC = true {product} {ergonomic}
JDK 11 from the above output on a machine having a 32 GB RAM {lp64_product} {lp64_product},Therefore, the initial heap is set to 1/64 of 32 GB (approximately 512 MB), and the maximum heap is set to 1/4 of 32 GB (8 GB).
V. Customize
If you think the default settings are not suitable for your application, I am very happy that HotSpot JVM is highly configurable in every field.
has three main types of configuration options:
- standard: basic startup options, such as Hot -classpath _span4 Jspan _span4 Jspan is very common.
- -X: is a non-standard option used to configure the general properties of the HotSpot JVM, such as controlling the maximum heap size ( -Xmx ); there is no guarantee that all HotSpot JVM implementations support these.
- -XX: is used to configure the advanced options of the advanced properties of the HotSpot JVM. According to the documentation, these contents are subject to change without notice, but the Java team has a well-managed process to delete them.
6. -XX options
Many -XX options can be further characterized as follows:
Product. These are the most commonly used -XX options.
Experimental. These are options related to the experimental features in the HotSpot JVM, which may not be ready for production. These options allow you to try the new HotSpot JVM features, and you need to unlock them by specifying the following:
-XX:+UnlockExperimentalVMOptions
For example, using the ZGC garbage collector in JDK 11 can be enabled like this:
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
Once an experimental feature is ready for production, the options that control it are no longer classified as experimental and do not need to be unlocked. The ZGC collector became the Product option in JDK 15.
Manageable. These options can also be set at runtime through the MXBean API or other JDK tools. For example, to display the locks held by the java.util.concurrent class in the HotSpot JVM thread dump,Please use:
java -XX:+PrintConcurrentLocks
Diagnostic. These options are related to accessing advanced diagnostic information about HotSpot JVM. These options require you to use the following in order to use:
-XX:+UnlockDiagnosticVMOptions
An example diagnostic option is:
-XX:+LogCompilation _prespot172 indicating a log file span_prespot172 , Which contains details of all optimizations made by the JIT compiler. You can check this output to understand which parts of the program have been optimized, and identify parts of the program that may not have been optimized as expected. LogCompilation The output is very detailed, but it can be visualized in tools such as JITWatch. It can tell you about method inlining, escape analysis, lock elision, and other optimizations made by the HotSpot JVM to the code you are running. Developmental. These options allow configuration and debugging of the most advanced HotSpot JVM settings,And you need to use a special HotSpot JVM to build and debug before you can access them.
7. Add and delete options
option switches are added and deleted after the arrival or deprecation of the main functions in HotSpot JVM. Here are some things worth noting.
- In JDK 9, many -XX:+Print... and -XX:+Trace... _span_span_span4 is replaced with log option 3span_span_span -XX:+Print... The option is used to control the unified logging subsystem introduced by JEP 158.
- After adding experimental ZGC, Epsilon, and Shenandoah garbage collector options, the number of options in JDK11 reached a peak, reaching a staggering 1504.
- With the removal of the concurrent mark scanning (CMS) garbage collector, the amount of data in JDK14 has dropped significantly, as described in JEP 363.
Figure 4. The total number of options in each version of OpenJDK (including products, experiments, diagnostics and development)
from JDK removed from JDK _openspan3 _ Table 1. 16 Previous HotSpot JVM options
Table 2. OpenJDK 17 Newly added HotSpot JVM options
5
373
p3 So how does the HotSpot JVM development team manage the deletion of options? Since JDK 9,The process of removing the -XX option was expanded to a three-step process: deprecation, obsolescence, and expiration, to warn users that their Java command line may soon need to be updated.
Let's see how HotSpot JVM operates on the -XX:+AggressiveOpts option, which was deprecated in JDK 11, was eliminated in JDK 12, and was finally eliminated in JDK 13. Expired.
The deprecated option . Although these options can be supported, a warning will be printed to let you know that support may be removed in the future, for example:
./jdk11/bin/java -XX:+AggressiveOptsOpenJDK 64-Bit Server VM warning: Option AggressiveOpts was deprecated in version 11.0 and will likely be removed in a future release.
Outdated option . Although these options have been removed,But it is still accepted on the command line. (Program) will print a warning to let you know that these options may not be accepted in the future, for example:
./jdk12/bin/java -XX:+AggressiveOptsOpenJDK 64-Bit Server VM warning: Ignoring option AggressiveOpts; support was removed in 12.0
Expired option. These are deprecated or obsolete options. The accept_until version is less than or equal to the current JDK version. When these options are used in an outdated JDK version, a warning will be printed, for example:
./jdk13/bin/java -XX:+AggressiveOptsOpenJDK 64-Bit Server VM warning: Ignoring option AggressiveOpts; support was removed in 12.0
completely failed (unavailable). Once you use an outdated configuration in an old version of JDK, HotSpot JVM will fail to start after passing this option and printing a warning, for example:
./jdk14/bin/java -XX:+ AggressiveOptsUnrecognized VM option'AggressiveOpts' Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.
Unfortunately, not all such options are in order. Way to exit. For example, JDK 9 abandoned support for a large number of options when introducing unified logging and the powerful -Xlog option, which is described in detail in Nicolai Palog's blog. There is also a page on the Java documentation website, refer to: Convert GC Logging Flags to Xlog.
9. Migrate to a higher version of JDK
So, are you ready to migrate the Java startup script command to a higher version of JDK? Maybe you used a unified startup script, which is full of options and configurations that you are not familiar with, and you are worried that adjustments will affect the stability of the application.
You can use JaCoLine, the Java command line checker to help you. Paste the command, select the target platform, and analyze how your configuration options will work. See Figure 5 .
Figure 5. Use JaCoLine to analyze command line options
10. JVM parameter configuration suggestions
Although there are some suggestions for JVM tuning in HotSpot, I believe Options can help you better understand the execution of the program and make wise configuration choices.
The following options are available in JDK 11 and later. I chose these switches because many developers have not yet moved to higher versions of Java. Keep in mind that these are optional; the default settings for HotSpot JVM are very good.
First, understand the memory usage. It is cheap to allocate memory in HotSpot JVM. Garbage collection cost refers to the consumption that expires later in the form of execution pause when the hot JVM cleans up objects that are no longer needed in the heap.
In terms of improving application performance and stability, understanding the heap allocation of the code and the resulting GC behavior may be the easiest problem to solve because of the difference between the heap and GC configuration and the allocation behavior of the application. Matching can cause excessive pauses, which can interrupt the application's progress.
Use the JaCoLine Statistics web page to confirm that configuring heap and GC logging are the most popular options in all command lines checked by JaCoLine.
To configure the heap, consider the answers to the following questions:
- What is the expected maximum heap memory usage under normal circumstances?
- -Xmx Set the maximum heap size, for example: -Xmx8g .
- -XX:MaxRAMPercentage=n sets the maximum heap as a percentage of the total RAM.
- How quickly do you expect the heap to reach its maximum value?
- -Xms Set the initial heap size to the maximum percentage of RAM. For example: -Xms256m .
In- _span_span3scentspan4
- If you want the heap to grow quickly,The initial heap can be set closer to the maximum heap.
To handle the OutOfMemory error, you need to consider how the HotSpot JVM should work when the application memory is insufficient.
- -XX:+ExitOnOutOfMemoryError tells the HotSpot JVM to exit when the first OutOfMemory error occurs. This is useful if the HotSpot JVM will restart automatically.
- -XX:+HeapDumpOnOutOfMemoryError dumps the contents of the heap to the java_pid.hprof file to help diagnose memory leaks.
- -XX:HeapDumpPath defines the heap dump path.
Second, select the garbage collector. JDK 11 ergonomics process on most hardware will select the G1GC collector by default,But it is not the only option in JDK 11 and higher.
Other available garbage collectors are:
- -XX:+UseSerialGC selects the serial collector, which performs all GC work on a single thread.
- -XX:+UseParallelGC selects a parallel (throughput) collector, which can use multiple threads to perform compression.
- -XX:+UseConcMarkSweepGC Select the CMS collector. Please note that the CMS collector has been deprecated in JDK 9 and removed in JDK 14.
- -XX:+UnlockExperimentalVMOptions -XX:+UseZGC Select the ZGC collector (experimental in JDK 11, standard feature in JDK 14 and later; so you don't need this switch).
You can find advice on choosing a collector for your application in the HotSpot Virtual Machine Garbage Collection Tuning Guide. This is the documentation version of JDK 11; if you are using a higher version of Java, please search for the updated documentation.
To avoid premature promotion,Please consider whether your application creates short-term objects at a high allocation rate. This may cause short-term objects to be promoted prematurely to the old generation heap space, where they will accumulate until a complete garbage collection is required.
- -XX:NewSize=n defines the initial size of the new generation.
- -XX:MaxNewSize=n defines the maximum size of the young generation.
- -XX:MaxTenuringThreshold=n is the maximum number of young generation sets that an object can survive before being promoted to the old generation.
To log memory usage and GC activity, do the following:
- use -XX:+UnlockDiagnosticVMOptions ‑XX:NativeSpanMemoryOptions ‑XX:NativeSpanMemoryOptions ‑XX:NativeMemoryTrackings+ _NativeSpotMemoryTrackings+ _NativeSpot4 Full details of time memory usage.
- Use the following command to enable GC logging:
- -Xlog:gc provides basic GC logging.
- -Xlog:gc* provides detailed GC log records.
Finally, understand how the JIT compiler optimizes your code. Once you are satisfied that the GC pause of your application is at an acceptable level, you can check whether the JIT compiler of the HotSpot JVM is optimizing the parts of the program that you think are important for performance.
enables a simple compilation log as follows:
- -XX:+PrintCompilation prints basic information about each JIT compilation to the console.
- -XX:+UnlockDiagnosticVMOptions ‑XX:+PrintCompilation ‑XX:+PrintInlining Add information about method inlining.
Example output:
java -XX:+PrintCompilation 77 1 3 java.lang.StringLatin1::hashCode: (42 bytesutils) 78 Concurrent: current. 3 3 jdk.internal.misc.Unsafe::getObjectAcquire (7 bytes) 80 java.lang.Object:: (1 bytes) 80 java.lang.String::isLatin1 (19) java.lang.String::isLatin1 (19) lang.String::hashCode (49 bytes)
The items in the output (from left to right) are as follows:
PrintCompilation is explained in the "Java JIT Part 1 Compiler" article .
Logging JIT information to the console is very useful for checking whether a method is JIT-compiled or inlined (or both), but if you want to have a deeper understanding of JIT optimization, you need to enable detailed logging.
Use -XX:+UnlockDiagnosticVMOptions ‑XX:+LogCompilation ‑XX:LogFile=jit.log to enable detailed compilation logging. It supports detailed XML format compilation log records, which can be analyzed in tools such as JITWatch. You can learn more about JITWatch from Ben Evans' "Understanding Java JIT Compilation with JITWatch, Part 1" and in Parts 2 and 3.
.