This outline

Journey Title JMAP: 5 JSTACK: 5 JSTAT: 5 Section JMAP: 5 JSTACK: 5 JSTAT: 5 Section JMAP: 5 JSTAT: 5 Section JMAP: 5 JSTAT: 5
  • Stage 1: A detailed introduction to the basic JVM tools
  • Stage 2: JVM optimization ideas
  • Stage 3: A real-world tuning case for the JVM

Stage 1: Introduction to basic JVM tools

Jmap –>Java memory mapping tool

JmapMemory Map for Java is used to generate heapdump snapshots commonly known as heapdumps or dump files. At the same time, it can also query finalize execution queue, Java heap and method area details, such as space usage, which collector is currently used, etc.

Simply put, it can be used to view heap memory information

Jmap command format

jmap option vmid

Option option

Jmap uses demo examples

Example: You can create a spring-Boot project and add a Web module to start it

Step 1: Start the spring-Boot project you created

Step 2: Enter the JPS command on the terminal

The jpsJava Virtual Machine Process Status Tool is a command provided by Java to display the PID of all current Java processes

Find the corresponding Java process number: 49150

Step 3: Enter the command in jmap format on the terminal

The first command: jmap-histo process number

Enter jmap-histo 49150 to see the result:

We found that there were too many things to print in the terminal, so we output the printed content to the file

Jmap-histo 49150 >./ jmaplog. TXT

JmapLog. TXT file is generated in the corresponding file path.

You can see the three pieces of content and the number on the left can be ignored

  1. Class name: indicates the class name
  2. Bytes: indicates the number of bytes
  3. Instances: indicates the number of instances

It simply prints the number of instances of each class, the total number of bytes used or the size of memory

The second command: jmap-heap process number

This command is used to display detailed information about the Java heap, such as which garbage collector is used, parameter configuration, generation status, etc. Let’s print it out

Enter jmap-heap 49150 to see the result:

If you have the same problem as me, you can take a look at this article, which should solve your problem, not of course best:

Error: Can’t attach Symbolicator to the process

JHSDB jmap –heap –pid 49150

$ jhsdb jmap --heap --pid 49150
Attaching to process ID 49150, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.011.+9-LTS-194

using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 1287651328 (1228.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 2048
   capacity = 2147483648 (2048.0MB)
   used     = 14274048 (13.61279296875MB)
   free     = 2133209600 (2034.38720703125MB)
   0.6646871566772461% used
G1 Young Generation:
Eden Space:
   regions  = 2
   capacity = 69206016 (66.0MB)
   used     = 2097152 (2.0MB)
   free     = 67108864 (64.0MB)
   3.0303030303030303% used
Survivor Space:
   regions  = 7
   capacity = 7340032 (7.0MB)
   used     = 7340032 (7.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 5
   capacity = 57671680 (55.0MB)
   used     = 4836864 (4.61279296875MB)
   free     = 52834816 (50.38720703125MB)
   8.386896306818182% used
Copy the code
The first piece of content: configuration information for the heap

  • MinHeapFreeRatio: the smallest percentage of free heap space, calculation formula is: HeapFreeRatio = (CurrentFreeHeapSize/CurrentTotalHeapSize) * 100, the value of the range of 0 to 100, the default value is 40. If HeapFreeRatio < MinHeapFreeRatio, you need to expand the heap after each garbage collection.

  • MaxHeapFreeRatio: the biggest percentage of free heap space calculation formula is: HeapFreeRatio = (CurrentFreeHeapSize/CurrentTotalHeapSize) * 100, the value of the range of 0 to 100, the default value is 70. If HeapFreeRatio > MaxHeapFreeRatio, you need to shrink the heap at the end of each garbage collection.

  • MaxHeapSize: maximum heap memory

  • NewSize: Space occupied by the new generation

  • MaxNewSize: specifies the maximum available space for the new generation

  • OldSize: Old age occupies space

  • NewRatio: Proportion of Cenozoic era, 2 means 1:2, one-third

  • SurvivorRatio: ratio of two Survivor zones to Eden zones, 8 indicates two Survivor zones :Eden = 2:8, meaning that one Survivor zone accounts for 1/10 of the young generation

  • MetaspaceSize: indicates the metasspace size

  • CompressedClassSpaceSize: Indicates the size of the compressed space

  • MaxMetaspaceSize: indicates the maximum metasize size

  • G1HeapRegionSize: Size of a Region in the G1 garbage collector

The second piece of content: information about the heap being used

Since I am using JDK 11, the default is G1 garbage collector, so the printed heap memory structure is different from JDK 8, you can analyze according to your own real scene, there is no need to be consistent with me

  • Capacity is the total Heap size. Used is the used Heap space. Free is the unused Heap space

  • G1 Young Generation: The size of Young space/Young Generation, which is divided into Eden area and Survivor area. The size of each area will not be described in detail here, but you can just look at the picture

  • G1 Old Generation: Part of the Old space/size of the Old Generation

The third command is jmap-dump process number

Jmap-dump :format=b,file=jmapDump 49150

A heap snapshot information file called jmapDump is generated in the corresponding directory

Setting up automatic download

We can also set up automatic download to dump files automatically when memory runs out

Tip: When the memory is very large, it may not be able to output

  1. – XX: + HeapDumpOnOutOfMemoryError set up automatic export
  2. – XX: HeapDumpPath =. / / / XXX XXX XXX path
See the jmapDump example

Creating a new test class in the test directory creates an object in an infinite loop and keeps it referenced by GC Roots without being recycled

Plus – XX: + HeapDumpOnOutOfMemoryError, XX: HeapDumpPath run after the two commands, when memory dump automatically


// Test the code

public class DumpTest {

    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        int i = 0;
        int j = 0;
        while (true) {
            list.add(new Teacher("teacher" + i++, i++));
            new Teacher("teacher"+ j--, j--); }}}class Teacher {
    private String name;
    private Integer age;
    public Teacher(String name, Integer age) {
        this.name = name;
        this.age = age; }}Copy the code

We add JVM parameters to the configuration of IDEA. For those who do not have JVM parameters, select the column in the red box below

-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumpTest.log

-Xms10M -XMx10m is used to generate OOM as soon as possible. After setting parameters, run the command to find OOM generated

The dumptest. log file has been automatically generated, so how can we analyze this file?

jvisualvm

The tool that comes with the JDK allows you to view this file by importing it

Pro tip: JdK11 does not support this tool, since the JDK on my computer is a dual version, I will switch to JDK8 and open JVisualVM for this demonstration

We enter JVisualVM in the terminal, open the tool, and open the downloaded dumptest.log file through file import

You can see that basic information, environment, system properties, and thread information on the heap snapshot are displayed

Let’s switch to the class window

Can know which several instances of the class most, occupy the space size, how much is the proportion, we can through this way, clearly know the heap distribution of instances of the class, if you find a particular instance of a class much more special, we can go to locate create instances of this class, to carry on the screen, we can find out the cause of the JVM memory surge through this method

The remaining jmap commands, and the remaining three jmap commands, are not shown here because they are not commonly used

Jstack –>Java stack trace tool

The jstackStack Trace for Java command is used to generate a thread snapshot of the JVM at the current time

Thread snapshot

A thread snapshot is a stack of methods being executed by each thread in the current JVM. A thread snapshot is usually generated to locate the cause of a thread’s long pause, such as interthread deadlocks, dead loops, and long suspends due to requests for external resources.

By looking at the call stack of individual threads when a thread pauses, you can get a sense of what the unresponsive thread is doing in the background, or what resources it is waiting for.

Jstack command format

jstack option vmid

Jstack example

This class is used to locate the cause of a thread’s long pause, such as interthread deadlock, so we simulate a thread deadlock, the following is a simple deadlock class:

/** * When an object of class DeadLock flag==1 (Td1) locks O1 and sleeps for 500 milliseconds * WHILE Td1 sleeps another object (Td2) whose flag==0 starts a thread and locks O2. 500 milliseconds * Td1 needs to lock O2 after sleep to continue. O2 is locked by TD2; * TD2 needs to lock O1 to continue after sleep, and O1 is already locked by TD1; * Td1 and TD2 wait for each other and need to obtain the resource locked by the other party to continue execution, thus deadlock. * /
public class DeadLockTest implements Runnable{

    public int flag = 1;
    
    // Static objects are shared by all objects of the class
    private static Object o1 = new Object(), o2 = new Object();
    @Override
    public void run(a) {
        System.out.println("flag=" + flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("1"); }}}if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("0"); }}}}public static void main(String[] args) {
        DeadLockTest td1 = new DeadLockTest();
        DeadLockTest td2 = new DeadLockTest();
        td1.flag = 1;
        td2.flag = 0;
        //td1,td2 are both in an executable state, but the JVM thread schedule is not determined which thread executes first.
        //td2's run() may run before Td1's run()
        new Thread(td1).start();
        newThread(td2).start(); }}Copy the code

So let’s run it, and we’re stuck here

We do not terminate the program, using jstack command to see how to check, first through the JPS command to find the Java process

And then look at it using the JStack process number

2021-04-27 23:42:14
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):

"Attach Listener" #15 daemon prio=9 os_prio=31 tid=0x00007fc476153800 nid=0x320b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #14 prio=5 os_prio=31 tid=0x00007fc4761b1800 nid=0x1003 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #13 prio=5 os_prio=31 tid=0x00007fc475108800 nid=0x3e03 waiting for monitor entry [0x0000700002560000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:39)
	- waiting to lock <0x0000000795946928> (a java.lang.Object)
	- locked <0x0000000795946938> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

"Thread-0" #12 prio=5 os_prio=31 tid=0x00007fc4768b8800 nid=0x3c03 waiting for monitor entry [0x000070000245d000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:27)
	- waiting to lock <0x0000000795946938> (a java.lang.Object)
	- locked <0x0000000795946928> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

"Service Thread" #11 daemon prio=9 os_prio=31 tid=0x00007fc476152800 nid=0x4203 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007fc476152000 nid=0x4403 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007fc476151000 nid=0x3903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007fc47687c800 nid=0x4503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007fc475032000 nid=0x4603 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007fc475031000 nid=0x4703 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007fc47582e800 nid=0x4807 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc47582a000 nid=0x4903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc475022000 nid=0x5103 in Object.wait() [0x0000700001939000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
	- locked <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:212)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fc476033800 nid=0x5203 in Object.wait() [0x0000700001836000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
	- locked <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=31 tid=0x00007fc476014800 nid=0x2b03 runnable

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fc47501b800 nid=0x1f07 runnable

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fc47501c000 nid=0x2103 runnable

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fc47501c800 nid=0x2303 runnable

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fc47501d800 nid=0x2a03 runnable

"VM Periodic Task Thread" os_prio=31 tid=0x00007fc475962000 nid=0x3b03 waiting on condition

JNI global references: 1579


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007fc4758254a8 (object 0x0000000795946928, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007fc475821408 (object 0x0000000795946938, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:39)
	- waiting to lock <0x0000000795946928> (a java.lang.Object)
	- locked <0x0000000795946938> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at com.project.mall.test.DeadLockTest.run(DeadLockTest.java:27)
	- waiting to lock <0x0000000795946938> (a java.lang.Object)
	- locked <0x0000000795946928> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
Copy the code

It prints information about threads in the process, such as the following two threads:

  • Thread-1: indicates the name of a Thread
  • Prio: indicates the priority
  • Os_prio: Specifies the thread priority of the OPERATING system
  • Tid: indicates the id of a thread
  • Nid: indicates the ID of the local thread
  • Java.lang.thread. State: indicates the status of a Thread

Further down, at the end of the printed message, we find that JStack has found a deadlock for us, saying it has found a deadlock at the Java level

In addition, jStack also prints the cause of the deadlock. For example, Thread 1 is waiting to acquire the lock 0x00007FC4758254A8, but the lock is now held by Thread 0. Thread-0 waits to acquire the lock 0x00007FC475821408, but the lock is now held by Thread-1

In addition to printing the cause of the deadlock, it also helps us to locate the number of Java lines of code:

Jvisualvm detects deadlocks

Deadlocks can also be quickly detected using JVisualVM, as shown in the figure below

The next thread to Dump is the id of the jStack process that executes what we said above

Things you must know about JVisualVM

Although JVisualVM can monitor the remote, it is generally not used because if the server is to be monitored, JMX’s port configuration is enabled when the server is started

However, it is impossible to open such an important port in the production environment, so jVisualVM is generally not used to monitor the JVM on the line, so the specific configuration mode is not expanded, but the development environment, test environment can be used according to the need, such as pressure test

Jstack finds the threads that consume the most CPU

Preparation: I directly take the development environment to simulate the demonstration, we look at the demonstration process

Use the top command to find the process with the highest CPU

Enter top to view the processes with the highest CPU usage

As shown in the figure, the Java process with the highest CPU usage is 18955

Run the top -p process NUMBER command

Run the top -p process number command to check the memory usage of the process. Enter top -p 18955 to check the memory usage

Press H to enter details of the process

Press H to view memory usage for each thread in the process

The PID on the far left is our thread ID

Follow the jstack command to find the code corresponding to the thread ID

  1. First, convert the thread ID to hexadecimal. Since the thread ID printed in jStack information is hexadecimal, convert 24091 to hexadecimal, which is 5e1b

  2. Executing jstack 18955 | grep – A 10 5 e1b, matches the thread line 10 lines of code, can be found from the stack to CPU soaring method is called

  3. Analyze the code printed on the stack, as shown below. This is just an example. Ignore the contents

"http-nio-8080-ClientPoller" #43 daemon prio=5 os_prio=31 cpu=24.76ms elapsed=327.74s tid=0x00007fd3f33d0800 nid=0x5e1b runnable  [0x000070000d58d000]
   java.lang.Thread.State: RUNNABLE
	at sun.nio.ch.KQueue.poll(java.base@11.011./Native Method)
	at sun.nio.ch.KQueueSelectorImpl.doSelect(java.base@11.011./KQueueSelectorImpl.java:122)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.011./SelectorImpl.java:124)
	- locked <0x00000007843563e8> (a sun.nio.ch.Util$2)
	- locked <0x0000000784356290> (a sun.nio.ch.KQueueSelectorImpl)
	at sun.nio.ch.SelectorImpl.select(java.base@11.011./SelectorImpl.java:136)
	at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:816)
	at java.lang.Thread.run(java.base@11.011./Thread.java:834)
Copy the code

Jinfo –>Java configuration information tool

JinfoConfiguration Info for Java is used to view and adjust JVM configuration parameters in real time

Viewing JVM parameters

Jinfo-flags Indicates the process ID

This command has the same problem as the jmap command failure above, and you need to switch to another JDK version

Viewing System Parameters

Jinfo can also use the -sysprops option to print out the contents of system.getProperties () for a virtual machine process, as shown in the figure below

VM Flags:
-XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:CICompilerCount=3 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=4 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=134217728 -XX:+ManagementServer -XX:MarkStackSize=4194304 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=1287651328 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=6973028 -XX:NonProfiledCodeHeapSize=244685212 -XX:ProfiledCodeHeapSize=0 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC
zhouxinzedeMacBook-Pro:~ zhouxinze$ jinfo -sysprops 84587
Java System Properties:
#Thu Apr 29 21:55:19 CST 2021
gopherProxySet=false
awt.toolkit=sun.lwawt.macosx.LWCToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=UTF-8java.class.path=/Users/zhouxinze/IdeaProjects/mall/target/classes\:/Users/zhouxinze/.m2/repository/org/springframework/b oot/spring-boot-starter-web/2.4. 5/spring-boot-starter-web-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter/2.4. 5/spring-boot-starter-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot/2.4. 5/spring-boot-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.4. 5/spring-boot-autoconfigure-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.4. 5/spring-boot-starter-logging-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/ch/qos/logback/logback-classic/1.23./logback-classic-1.23..jar\:/Users/zhouxinze/.m2/repository/ch/qos/logback/logback-core/1.23./logback-core-1.23..jar\:/Users/zhouxinze/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.133./log4j-to-slf4j-2.133..jar\:/Users/zhouxinze/.m2/repository/org/apache/logging/log4j/log4j-api/2.133./log4j-api-2.133..jar\:/Users/zhouxinze/.m2/repository/org/slf4j/jul-to-slf4j/1.730./jul-to-slf4j-1.730..jar\:/Users/zhouxinze/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3. 5/jakarta.annotation-api-1.3. 5.jar\:/Users/zhouxinze/.m2/repository/org/yaml/snakeyaml/1.27/snakeyaml-1.27.jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.4. 5/spring-boot-starter-json-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.114./jackson-databind-2.114..jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.114./jackson-annotations-2.114..jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.114./jackson-core-2.114..jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.114./jackson-datatype-jdk8-2.114..jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.114./jackson-datatype-jsr310-2.114..jar\:/Users/zhouxinze/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.114./jackson-module-parameter-names-2.114..jar\:/Users/zhouxinze/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.4. 5/spring-boot-starter-tomcat-2.4. 5.jar\:/Users/zhouxinze/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.045./tomcat-embed-core-9.045..jar\:/Users/zhouxinze/.m2/repository/org/glassfish/jakarta.el/3.03./jakarta.el-3.03..jar\:/Users/zhouxinze/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.045./tomcat-embed-websocket-9.045..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-web/5.36./spring-web-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-beans/5.36./spring-beans-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-webmvc/5.36./spring-webmvc-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-aop/5.36./spring-aop-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-context/5.36./spring-context-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-expression/5.36./spring-expression-5.36..jar\:/Users/zhouxinze/.m2/repository/org/slf4j/slf4j-api/1.730./slf4j-api-1.730..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-core/5.36./spring-core-5.36..jar\:/Users/zhouxinze/.m2/repository/org/springframework/spring-jcl/5.36./spring-jcl-5.36..jar
java.vm.vendor=Oracle Corporation
sun.arch.data.model=64
catalina.useNaming=false
java.vendor.url=https\://openjdk.java.net/
user.timezone=Asia/Shanghai
java.vm.specification.version=11
os.name=Mac OS X
sun.java.launcher=SUN_STANDARD
user.country=CN
sun.boot.library.path=/Library/Java/JavaVirtualMachines/jdk-11.011..jdk/Contents/Home/lib
spring.application.admin.enabled=true
sun.java.command=com.mall.MallApplication
com.sun.management.jmxremote=
jdk.debug=release
sun.cpu.endian=little
spring.liveBeansView.mbeanDomain=
user.home=/Users/zhouxinze
user.language=zh
java.specification.vendor=Oracle Corporation
java.version.date=2021-04-20
java.home=/Library/Java/JavaVirtualMachines/jdk-11.011..jdk/Contents/Home
file.separator=/
spring.output.ansi.enabled=always
java.vm.compressedOopsMode=Zero based
line.separator=\n
java.specification.name=Java Platform API Specification
java.vm.specification.vendor=Oracle Corporation
FILE_LOG_CHARSET=UTF-8
java.awt.graphicsenv=sun.awt.CGraphicsEnvironment
java.awt.headless=true
user.script=Hans
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
java.runtime.version=11.011.+9-LTS-194
spring.jmx.enabled=true
path.separator=\:
os.version=10.156.
java.runtime.name=Java(TM) SE Runtime Environment
file.encoding=UTF-8
spring.beaninfo.ignore=true
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.vendor.version=18.9
java.vendor.url.bug=https\://bugreport.java.com/bugreport/
java.io.tmpdir=/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/
catalina.home=/private/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/tomcat8080.5214054096261208489.
java.version=11.011.
os.arch=x86_64
java.vm.specification.name=Java Virtual Machine Specification
PID=84587
java.awt.printerjob=sun.lwawt.macosx.CPrinterJob
sun.os.patch.level=unknown
CONSOLE_LOG_CHARSET=UTF-8
catalina.base=/private/var/folders/qz/gl34hk1x5yv5wb0l6tsyq_x80000gp/T/tomcat8080.5214054096261208489.
java.vendor=Oracle Corporation
java.vm.info=mixed mode
java.vm.version=11.011.+9-LTS-194
java.rmi.server.randomIDs=true
sun.io.unicode.encoding=UnicodeBig
java.class.version=55.0
Copy the code

Important: Jstat –>JVM statistics monitoring tool

The jstatJVM Statistics Monitoring Tool is a command line Tool used to monitor various JVM running status information.

It can display runtime data from local or remote JVM processes such as classloading, memory, garbage collection, just-in-time compilation, and so on. On servers with no GUI graphical interface and only a text-only console environment, it can be a common tool for locating virtual machine performance problems at runtime.

Format of the jstat command

Jstat [- command options][vmid] [Interval][Number of queries]

Command Options list

The first feature: garbage collection statistics

Jstat – INDICATES the NUMBER of the GC process

  • S0C: indicates the size of the current survivor0 zone
  • S1C: indicates the size of the current survivor1 zone
  • S0U: survivor0 specifies the used size of the zone
  • S1U: survivor1 specifies the used size of the zone
  • EC: Indicates the size of Eden
  • EU: Usage size of Eden area
  • OC: The size of the old days
  • MC: The size of the meta space
  • MU: indicates the usage size of the metspace
  • CCSC: indicates the size of the compression space
  • CCSU: indicates the used size of the compression space
  • YGC: The number of Minor GC’s that have occurred since the program was run
  • According to the example: 4 minorgcs take 0.021 seconds
  • FGC: The number of Full GC’s that have occurred since the program was run
  • FGCT: Total time consumed by Full GC
  • CGC: G1 collects Mixed GC times concurrently
  • CGCT: Time taken by G1 to collect Mixed GC concurrently
  • GCT: Total garbage collection consumption

Second function: heap memory statistics

Jstat -gccapacity Indicates the process ID

  • NGCMN: minimum capacity of young generation
  • NGCMX: maximum capacity of young generation
  • NGC: current young generation capacity
  • S0C, S1C, and EC are the same as in the preceding GC information
  • OGCMN: minimum capacity in the old age
  • OGCMX: Old age maximum capacity
  • OGC: Current age size
  • OC: Same as in the ABOVE GC information
  • MCMN: indicates the minimum capacity of the metacase
  • MCMX: indicates the maximum capacity of the meta-space
  • MC: indicates the current capacity of the meta space
  • CCSMN: indicates the minimum size of compressed pointer space
  • CCSMX: maximum size of compressed pointer space
  • CCSC: current size of compressed pointer space
  • YGC, FGC, and CGC are the same as in the above GC information

Similarly, if you want to look at specific monitoring content, here is the graph. Note that different garbage collectors may print different content, so I won’t mention it here

JVM health estimates

Remember the interval that we used to estimate JVM performance

Here’s an example: If we want to perform jstat gc statistics once every 1s, for a total of 10 times, we can do this by executing jstat -GC 85164 1000 10, and we can get the following statistics: When you’re writing your test class, you can monitor the data by constantly new objects

What can we see from this picture?

  1. Predict the growth rate of young generation objects
  2. MinorGC triggering frequency and Average DURATION Average duration =YGCT/YGC, total MinorGC duration Calculated by dividing MinorGC counts. The total MinorGC duration is calculated according to the system interval
  3. After each Minor GC, observe the changes of EU, S0U, S1U, and OU after each Minor GC to deduce the number of objects entering the old age, and then determine the growth rate of old age objects based on the Minor GC frequency
  4. Full GC trigger frequency and average time FGCT/FGC

Stage 2: Common JVM tuning ideas

Combined with the rules of moving objects to the old age, the following optimization ideas can be used:

  1. In simple terms, try to keep the number of surviving objects after each Minor GC less than 50% of the Survivor zone to avoid premature aging due to dynamic age determination. Try to keep them alive in the young generation and minimize the frequency of Full GC. Full GC can have a serious impact on JVM performance in high concurrency systems. Normally create objects are rarely per second, but a certain period of time, suddenly the concurrency value rise, leading to a new object created by too quickly, easily because of the dynamic mechanism into the old s early age judgment, so this kind of circumstance, to adjust the size of the young generation, keep these objects as much as possible in the young generation, because these are the object of life in death

  2. The object of large object needs a large number of continuous memory space Such as strings, arrays, to enter the old s as soon as possible, because the alternative is to mark – young replication algorithm, large objects in copy will consume a lot of performance, so to use as soon as possible into the old s – XX: PretenureSizeThreshold set size, Objects larger than this size go directly to the old generation, not to the young generation, and this parameter is only useful for Serial and ParNew GC collectors

Combined with the number of frequent occurrences of Full GC, the following optimization ideas are mainly used

  1. In addition to the normal Full GC that occurs due to insufficient old age space, there is also the old age space guarantee mechanism. Before each Minor GC, the JVM calculates the free space of the old age. If the free space is less than the sum of all the existing objects in the young generation, the JVM will calculate the free space of the old age. -xx: -handlePromotionFailure JDK 1.8 default setting is set. This is a guarantee that if the remaining age space is less than the average of objects that have entered the age after each Minor GC, A Full GC will occur, and then a Minor GC will occur, and then a Full GC will occur again, so that a Full Minor GC will occur twice, and then a Minor GC will occur

  2. Insufficient meta space can result in excess Full GC, resulting in frequent Full GC

  3. Shows that calling System.gc causes extra Full GC, which can be disabled by using the -xx :+DisableExplicitGC parameter, which has no effect on system. gc

Stage 3: The JVM tuning case

Example of JVM tuning: Full GC occurs frequently online

Suppose you have a list of JVM parameters that simulate a real-world JVM environment

-Xms1536M -Xmx1536M -Xmn512M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly
Copy the code

If you are not aware of these commands, please refer to the following instructions and skip the following section:

  1. -xMS1536m: Sets the initial memory of the JVM to 1536M. This value can be set to the same as -xmx to avoid the JVM reallocating memory after each garbage collection.
  2. -XMx1536m: Sets the maximum available memory of the JVM to 1536M
  3. -Xmn512M: Sets the size of the young generation to 512 MB
  4. -xss256K: Sets the stack size of each thread to 256K
  5. -xx :SurvivorRatio=6: Sets the size ratio of Eden zone to Survivor zone in the young generation. If set to 6, the ratio of two Survivor zones to one Eden zone is 2:6, and one Survivor zone accounts for 1/8 of the whole young generation
  6. -xx :MetaspaceSize=256M: Set the MetaspaceSize size to 256M
  7. -xx :MaxMetaspaceSize=256M: Set the maximum metasize size to 256M
  8. -xx :+UseParNewGC: Sets the young garbage collector to ParNew
  9. -xx :+UseConcMarkSweepGC: Sets the old age garbage collector to CMS
  10. CMS – XX: CMSInitiatingOccupancyFraction = 75: set in the old s memory utilization rate reach 75% of the time to start the GC because CMS will have floating garbage, so generally start early GC
  11. – XX: + UseCMSInitiatingOccupancyOnly: only set collection threshold of 75% of the specified above, if not specified, the JVM is only used for the first time setting, subsequent automatic adjustment, general and a command combination

Hypothesis 1: Full GC occurs frequently due to dynamic age judgment mechanism

How to tune Full GC due to the dynamic age determination mechanism?

  1. The key point of dynamic age judgment mechanism is the space size of the young generation, so the first is to adjust the space size of the young generation

  2. If it is a large quantity of concurrent system, we can set the value of the little CMSInitiatingOccupancyFraction, avoid to produce Serial Old collector, but if it is a small amount of concurrent systems, We can set up CMSInitiatingOccupancyFraction value, make full use of heap space

So, after tuning, the JVM parameters look like this: Set the CMS memory usage threshold to 90% to make full use of the Old space. If you have a lot of concurrency, you may need to lower this threshold to avoid using the Serial Old collector because of concurrency conflicts

-Xms1536M -Xmx1536M -Xmn1024M -Xss256K -XX:SurvivorRatio=6 -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=90 -XX:+UseCMSInitiatingOccupancyOnly
Copy the code

Hypothesis 2: Full GC occurs frequently due to the old chronospatial guarantee mechanism

If you set the age to a smaller size, you could easily have Full GC occurring frequently due to the age guarantee mechanism. The key to the age guarantee mechanism is the average size of objects entering the age during each Minor GC. So we want to control the average size of objects that enter the old age after each Minor GC

Determine the distribution of objects in memory

Use the jmap-histo process number command to observe the distribution of objects in memory, and observe whether there are more centralized objects, because if the system is a high amount of concurrency, the interface is likely to be centralized, and the creation of objects is also centralized, so you can use the method of high CPU consumption, That is, the hot method, the object that occupies more memory these two aspects to analyze

  • The method of finding hot spots with jVisualVM’s sampler in JDK8 is not supported in JDK11

  • Use the Jmap-HISto process number to observe the objects that occupy a lot of memory. Start with the objects in your project

To optimize the direction

  1. If you are creating objects in a cycle, try to limit the number of cycles, for example, 5000 records at a time. If these records are loaded into memory, you will need to create a lot of objects. If these objects go through the Minor GC, they will be prone to Full GC due to the old age space allocation guarantee. Thus reducing the number of objects created

  2. The fastest and most efficient way is to increase the configuration of the physical machine and increase the memory of the whole heap as the budget allows. If conditions permit, it is also a good way

This paper summarizes

Well, that’s all for this article, in which we also went through the following steps

  1. The first stage: describes the basic use of jmap, jinfo, jstack, jstat commands, and what are their functions
  2. Phase 2: Describes the direction of JVM optimization thinking, which can be divided into two directions
  3. Stage 3: According to the case of Full GC frequently occurring online, how are various commands used together

There are a lot of commands, you can collect more, wait until the next problem, you can directly turn out the use

omg

Finally, if you feel confused about the article, please leave a comment immediately. If you think I have something to ask for a thumbs up 👍 for attention ❤️ for share 👥 is really very useful for me!!