08 Jvm Startup Parameters Detailed Broad Vision and Thorough Understanding Accumulates Wisdom

08 JVM Startup Parameters Detailed- Broad Vision and Thorough Understanding Accumulates Wisdom #

As a general-purpose virtual machine, JVM can adjust its running state and behavior through different JVM parameters specified when starting the Java command. These parameters include memory management and garbage collection (GC) algorithms, debugging and diagnostic information addition and processing, etc. This section provides a summary of JVM parameters, and detailed parameters related to GC will be explained and analyzed in later GC chapters.

The format for directly starting a Java program from the command line is:

java [options] classname [args]

java [options] -jar filename [args]

Where:

  • The [options] section is called “JVM options” and corresponds to “VM options” in IDE. It can be viewed using jps -v.
  • The [args] section refers to “arguments passed to the main function” and corresponds to “Program arguments” in IDE. It can be viewed using jps -m.

If you are using programs such as Tomcat that come with startup scripts like startup.sh, it is common to put relevant parameters into an environment variable defined by the script, and then the script will add all the parameters in the JAVA_OPTS variable to the command at the appropriate position when starting the JVM.

If you are running in an IDE such as IntelliJ IDEA, you can see two input fields for VM options and program arguments in “Run/Debug Configurations” and enter the parameters directly.

73146375.png

In the above image, two VM parameters are inputted, both of which are environment variables. One specifies the use of UTF-8 for file encoding, and the other sets the value of environment variable ‘a’ to 1.

Java and JDK built-in tools specify parameters using ‘-’, regardless of whether they are long parameters or short parameters. Sometimes, there is no strict distinction required for JVM startup parameters and Java program startup parameters. It is sufficient to have a rough understanding that they are concepts related to each other.

JVM startup parameters can be briefly categorized based on their form:

  • Parameters starting with - are standard parameters that all JVM implementations must support and are backward compatible.
  • Parameters starting with -X are non-standard parameters that are mostly passed to the JVM. The default JVM implementation supports these parameters’ functionality, but it is not guaranteed that all JVM implementations satisfy them, nor are backward compatible.
  • Parameters starting with -XX: are non-stable parameters, specially used to control JVM behavior, which are specific to the JVM implementation and may be removed in the next version.
  • The -XX:+-Flags format represents a boolean switch.
  • The -XX:key=value format specifies the value of a specific option.

In fact, if you directly input ‘java’ in the command line and press enter, you will see a list of available command line options for the ‘java’ command:

$ java
Usage: java [-options] class [args...]
           (to execute a class)
   or  java [-options] -jar jarfile [args...]
           (to execute a jar file)
where options include:
    -d32      use a 32-bit data model if available
    -d64      use a 64-bit data model if available
    -server   to select the "server" VM
                      The default VM is server,
                      because you are running on a server-class computer.
    -cp <class search path of directories and zip/jar files>
    -classpath <class search path of directories and zip/jar files>
                 A : separated list of directories, JAR archives,
                 and ZIP archives to search for class files.
    -D<name>=<value>
                 set a system property
    -verbose:[class|gc|jni]
                 enable verbose output
    -version      print product version and exit
    -version:<value>
                 Warning: this feature is deprecated and will be removed
                 in a future release.
                 specify system version required by the application
    -showversion  print product version and continue
    -jre-restrict-search | -no-jre-restrict-search
                 Warning: this feature is deprecated and will be removed
                 in a future release.
                 include/exclude user private JREs in the version search
    -? -help      print this help message
    -X            print non-standard options help
    -ea[:<packagename>...|:<classname>]
    -enableassertions[:<packagename>...|:<classname>]
                 enable assertions at specified granularity
    -da[:<packagename>...|:<classname>]
    -disableassertions[:<packagename>...|:<classname>]
                 disable assertions at specified granularity
    -esa | -enablesystemassertions
                 enable system assertions
    -dsa | -disablesystemassertions
                 disable system assertions
    -agentlib:<libname>[=<options>]
                 load native agent library <libname>, e.g. -agentlib:hprof
                 see also -agentlib:jdwp=help and -agentlib:hprof=help
    -agentpath:<pathname>[=<options>]
                 load native agent library by full pathname
    -javaagent:<jarpath>[=<options>]
Load Java programming language agent, please refer to java.lang.instrument
-splash: <imagepath>
Display the startup screen with the specified image

For more information, please see http://www.oracle.com/technetwork/java/javase/documentation/index.html.

### 7.1 Setting System Properties

When passing parameters to a Java program, the most common methods are:

  * System properties, sometimes called environment variables. For example, to pass specified system property parameters to the JVM, use the format `-Dkey=value`. In this case, if the system's environment variable does or does not specify this parameter, the specified parameter will be used.
  * Command line arguments, directly added to the command. For example, running the Hello class and passing 2 parameters "kimm" and "king": `java Hello kimm king`. Then, in the main method of the Hello class, you can access the string array of parameters, which contains the two strings "kimm" and "king".

For example, setting the commonly used $JAVA_HOME is an environment variable. As long as this environment variable exists in the current command execution context, any program started can access this parameter through the relevant API in Java. For example, in Java:

`System.getProperty("key")` can be used to get the value of this variable. This allows multiple different application processes to share these variables without having to set them repeatedly, and it can also simplify the length of the Java command line (consider how scary it is to configure 50 parameters, but if they are placed in environment variables, the input characters required for startup can be simplified). In addition, since the key-value pairs of environment variables do not rely on the order of parameters, whether they are configured in the environment context or specified through `-D` at runtime, the order of parameters does not matter. On the other hand, command line parameters must be careful about the order, as incorrect order can lead to program errors.

For example, specifying the random number entropy source:

JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"


There are also some common settings:

-Duser.timezone=GMT+08 // Set the user’s time zone to GMT+08 -Dfile.encoding=UTF-8 // Set the default file encoding to UTF-8


To view all default system properties, you can use the command:

$ java -XshowSettings:properties -version Property settings: awt.toolkit = sun.lwawt.macosx.LWCToolkit file.encoding = UTF-8 file.encoding.pkg = sun.io file.separator = / gopherProxySet = false java.awt.graphicsenv = sun.awt.CGraphicsEnvironment java.awt.printerjob = sun.lwawt.macosx.CPrinterJob java.class.path = . java.class.version = 52.0 … (omitted several lines)


You can also view VM settings:

$ java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 1.78G Ergonomics Machine Class: server Using VM: Java HotSpot(TM) 64-Bit Server VM …


To view the default display language settings of the current JDK/JRE:

java -XshowSettings:locale -version Locale settings: default locale = 中文 default display locale = 中文 (中国) default format locale = 英文 (中国)

available locales = , ar, ar_AE, ar_BH, ar_DZ, ar_EG, ar_IQ, ar_JO,
    ar_KW, ar_LB, ar_LY, ar_MA, ar_OM, ar_QA, ar_SA, ar_SD,
...

In addition, there are common scenarios where we pass data to Java programs using system property settings instead of directly using program parameters. For example, when using the mvn script to compile and execute, if you don't want to compile and execute the unit test code:

> $ mvn package -Djava.test.skip=true

Or

> $ mvn package -DskipTests

Many places use system property settings to pass data to Java programs instead of directly using program parameters.

### 7.2 Options related to Agents

Agent is a black technology in the JVM that can do many things in a non-intrusive way, such as injecting AOP code, performing statistics, etc. The permissions are very powerful. Here is a brief introduction to the configuration options, and detailed functionality will be discussed in subsequent chapters.

The syntax for setting the agent is as follows:

  • -agentlib:libname[=options] enables the agent in native mode, referring to the LD_LIBRARY_PATH path.
  • -agentpath:pathname[=options] enables the agent in native mode.
  • -javaagent:jarpath[=options] enables an external agent library, such as pinpoint.jar, etc.
  • -Xnoagent disables all agents.

The following example enables CPU usage time sampling:

JAVA_OPTS="-agentlib:hprof=cpu=samples,file=cpu.samples.log"

Here, hprof is a built-in performance analyzer in the JDK. cpu=samples samples the proportion of time consumed by various methods, and the analysis results will be output to a file when the Java process exits.

7.3 JVM Running Modes #

There are two running modes for the JVM:

  • -server: Sets the JVM to server mode. It starts slower but has high runtime performance and memory management efficiency, suitable for production environments. In JDK environments with 64-bit capabilities, this mode will be enabled by default and the -client parameter will be ignored.
  • -client: Before JDK 1.7, the default value on 32-bit x86 machines was -client. Sets the JVM to client mode. It starts faster but has lower runtime performance and memory management efficiency, usually used for client applications or PC application development and debugging.

In addition, we know that after the JVM loads bytecode, it can interpret and execute it, or compile it into native code before executing it. Therefore, the JVM can be configured to handle bytecode in different modes:

  • -Xint: In interpreted mode, the -Xint flag forces the JVM to interpret and execute all bytecode, which will slow down the runtime speed, typically by a factor of 10 or more.
  • -Xcomp: The -Xcomp parameter is the opposite of -Xint. The JVM compiles all bytecode into native code the first time it is used, thus maximizing optimization.
  • -Xmixed: -Xmixed is the mixed mode, which mixes interpretation and compilation, and the JVM decides on its own. This is the default and recommended mode for the JVM. We can see information like mixed mode using java -version.

Example:

JAVA_OPTS="-server"

7.4 Setting Heap Memory #

The memory settings of the JVM are the most important parameter settings and the focus of GC analysis and tuning.

Total JVM memory = Heap + Stack + Non-heap + Off-heap memory.

Related parameters:

  • -Xmx: Specifies the maximum heap memory. For example, -Xmx4g limits the maximum value of the Heap section to 4g. This memory does not include stack memory or memory used outside the heap.
  • -Xms: Specifies the initial size of the heap memory space. For example, -Xms4g. And the specified memory size is not the actual initial value allocated by the operating system, but rather planned by GC, which is allocated when used. On dedicated servers, -Xms and -Xmx should be kept consistent, otherwise there may be several FullGCs just after the application starts. When the two are configured differently, heap memory expansion may cause performance fluctuations.
  • -Xmn: Equivalent to -XX:NewSize, which should not be set when using the G1 garbage collector. It can be set in other specific business scenarios. The official recommendation is to set it to 1/2 ~ 1/4 of -Xmx.
  • -XX:MaxPermSize=size: This was used before JDK 1.7. Java 8 allows unlimited Metaspace by default, so this parameter is invalid.
  • -XX:MaxMetaspaceSize=size: Java 8 does not limit the Metaspace by default, so it is generally not allowed to set this option.
  • XX:MaxDirectMemorySize=size: The maximum off-heap memory that the system can use. This parameter has the same effect as -Dsun.nio.MaxDirectMemorySize.
  • -Xss: Sets the number of bytes for each thread stack. For example, -Xss1m specifies a thread stack of 1MB, which is equivalent to -XX:ThreadStackSize=1m.

I want to particularly mention off-heap memory, which is memory not on the heap. We can check it using tools like jconsole and jvisualvm.

RednaxelaFX mentioned:

In a Java process, there are many things that can allocate native memory, especially programs using third-party native libraries.

In addition to GC heap = Java heap + Perm Gen (JDK <= 7), Java thread stack = Java thread count * Xss, other thread stack = other thread count * stack size, CodeCache, and other things, HotSpot VM has many data structures that reside in memory, such as StringTable, SymbolTable, SystemDictionary, CardTable, HandleArea, JNIHandleBlock, etc. In addition, JIT compilers and GC may also allocate additional temporary native memory when they are working — all these are native memory allocated by HotSpot VM itself. In the JDK implementation, some features may also allocate native memory temporarily or for a long time.

Then there is the native part of third-party libraries that allocate native memory.

“Direct Memory” generally refers to the native memory allocated by Java NIO’s Direct-X-Buffer (such as DirectByteBuffer). If we use frameworks like Netty, it will generate a large amount of off-heap memory.

Example:

JAVA_OPTS="-Xms28g -Xmx28g"

Best Practices #

How much xmx is appropriate? #

From the above analysis, it can be seen that the system uses a large amount of off-heap memory, which is much wider than the range covered by xmx and xms that we usually talk about. So we need to leave room when setting the memory.

In fact, personally, I recommend configuring 70-80% of the available memory in the system or container. For example, if the system has 8GB of physical memory, the system itself may use a bit, and there may be about 7.5GB available. In that case, it is recommended to configure

-Xmx6g Note: xmx: 7.5GB * 0.8 = 6GB, if there are clear places in the system that use off-heap memory, you still need to further reduce this value.

To give a specific example, in several different-sized companies with different development stages and different levels of development maturity that I have worked in, I have found a common problem with JVM. The JVM instance often crashes suddenly, which may take three days or two weeks, and the exception information is very clear, which is OutOfMemoryError due to memory overflow.

The operations personnel constantly increase the heap memory or the physical memory of the cloud host, which is of no avail, at most prolonging this process.

Everyone suspects memory leaks, but looking at the GC log, it is actually quite normal, and there is no problem in the system’s performance testing environment. As a result, there are constantly conflicts and conflicts between development and operations.

One of the operations colleagues, in order to alleviate the problem, gradually expanded a low-stress server from 2 to 15 through observation for more than a month, because a few servers crashed randomly every day, and he needed to ensure that other machines could provide continuous service during the time when the system notified him to handle it.

Everyone has made a lot of efforts, made some technical explorations, and came up with many tricks, but the problem was not solved, that is, no value was created.

Later, after I investigated in depth, I solved the problem in a few minutes, created value for the technology, compressed the servers back to 2, and ensured the stable operation of the system, the continuous availability of the business, and the value brought by cost reduction, which also gained recognition from the business and customers.

So where is the actual problem? One cloud host with 4GB or 8GB of memory, in order to maximize the use of memory by JVM, the person responsible for service deployment directly configured xmx4g or xmx8g. Because he did not know that the memory configured by xmx is not necessarily equal to the maximum memory used by the JVM. I asked him to set xmx6g for the 8GB memory cloud host, and there have been no more problems since then. Also, he observed that the Java process used a little over 7GB of memory (the heap used up to 6GB, and the Java process itself and off-heap space also need to use memory, but these memories are not within the scope of xmx), which does not include the 6GB of memory set by xmx.

Should xmx and xms be configured consistently? #

In general, our servers are dedicated, which means that one machine (could also be a cloud host or a docker container) only deploys one Java application. In such cases, it is recommended to configure them consistently. The advantage is that there is no need to dynamically allocate. If there is insufficient memory (as in the case above), it is known from the beginning.

In production or performance testing environments, one of the important sources of data for us to analyze and determine problems is the GC log. JVM startup parameters provide us with options for controlling the output of GC log.

  • -verbose:gc: Used in combination with other GC parameters to output detailed GC information in the GC log. This includes the size of each memory pool before and after each GC, the size of the heap memory, the size promoted to the old generation, and the time consumed. This parameter supports dynamic switching during runtime. For example, using jcmd, jinfo, and other clients that use JMX technology.
  • -XX:+PrintGCDetails and -XX:+PrintGCTimeStamps: Print GC details and timestamps of when they occurred. Please pay attention to our subsequent GC course chapters.
  • -Xloggc:file: Similar to -verbose:gc, but records the relevant information of each GC event to a file. It is best to locate the file locally to avoid potential network issues. If it appears in the command line together with -verbose:gc, priority is given to -Xloggc.

Example:

export JAVA_OPTS="-Xms28g -Xmx28g -Xss1m \
-verbosegc -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/"

7.6 Specifying Garbage Collector Parameters #

The garbage collector is one of the core components for JVM performance analysis and tuning. It has been heavily developed and improved in recent JDK versions. By using different GC algorithms and parameter combinations, along with other tuning techniques, we can precisely optimize the system for best performance.

The following parameters specify the specific garbage collector. More details will be explained in the next section:

  • -XX:+UseG1GC: Use the G1 garbage collector
  • -XX:+UseConcMarkSweepGC: Use the CMS garbage collector
  • -XX:+UseSerialGC: Use the serial garbage collector
  • -XX:+UseParallelGC: Use the parallel garbage collector

7.7 Parameters for Special Situation Scripts #

In addition to the JVM parameters mentioned above, there are also parameters used for providing diagnostic information when problems occur.

  • -XX:+HeapDumpOnOutOfMemoryError: Automatically dumps the heap memory when an OutOfMemoryError occurs. This option is not expensive at runtime, so it can be used on production machines. Usage example: java -XX:+HeapDumpOnOutOfMemoryError -Xmx256m ConsumeHeap
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid2262.hprof ...
......
  • -XX:HeapDumpPath: Used in conjunction with HeapDumpOnOutOfMemoryError. Specifies the directory to save the dump file when a memory overflow occurs. If not specified, the default is the working directory of the Java program. Usage example: java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/ ConsumeHeap. The automatically dumped hprof file will be stored in the /usr/local/ directory.

  • -XX:OnError: Executes a script when a fatal error occurs. For example, write a script to record the error time, execute some commands, or curl a URL for online alerts. Usage example: java -XX:OnError="gdb - %p" MyApp. You may notice the %p format string, which represents the process ID.

  • -XX:OnOutOfMemoryError: Executes a script when an OutOfMemoryError occurs.

  • -XX:ErrorFile=filename: Specifies the log file for fatal errors, accepting absolute or relative paths.

This section provides only a brief introduction to the JVM parameters. In fact, there are many more parameters related to GC garbage collectors, which will be explained and analyzed in detail in the next section.

References #