01 preface

We have learned about the theory of the JVM, and this chapter will focus on the practical aspects of the JVM.

02 Class loading mechanism

After the Java source code is compiled into bytecode by the compiler, it ultimately needs to be loaded into the virtual machine before it can run. The virtual machine loads the data describing the Class from the Class file to the memory, verifies, transforms, and initializes the data, and finally forms Java types that can be directly used by virtual machines. This is the Class loading mechanism of virtual machines.

2.1 Class loading timing

When a type is loaded into vm memory and unloaded from memory, 2. Its life cycle must go through Loading, Verification, Preparation, Resolution, Initialization, Using and Unloading. The three parts of validation, preparation and parsing are collectively referred to as Linking. The sequence of these seven stages is shown below.

In the figure above, the order of the load, validate, prepare, initialize, and unload phases is determined. The loading process of a type must begin in this order, while the parsing phase may not: It can be started after the initialization phase in some cases to support the Runtime binding feature of the Java language (also known as dynamic binding or late binding).

The Java Virtual Machine Specification does not enforce the conditions under which to begin the first phase of the classloading process, “loading,” and leaves this up to the implementation of the virtual machine.

But for the initialization phase, the Java Virtual Machine Specification specifies that there are only six cases in which classes must be “initialized” immediately (loading, validation, and preparation naturally need to begin before then) :

  • Four bytecode instructions, new, getstatic, putstatic, or Invokestatic, are encountered;

  • When a java.lang.Reflect method is used to make a reflection call to a class;

  • When initializing a class, if the parent class is not initialized, the initialization of the parent class must be triggered first.

  • When a VM starts, the user needs to specify a main class to execute. The VM initializes this class first.

  • When using JDK 1.7 dynamic language support, if a Java lang. Invoke. MethodHandle instance the final analytical results

  • REF_getStatic, REF_putStatic, REF_invokeStatic method handle, and the corresponding class of this method handle is not initialized.

  • When an interface defines a new default method in JDK 8, if there is an implementation of the interface. Class is initialized, so the interface should be initialized before it.

The Java Virtual Machine Specification uses a very strong qualifier “have and only” for each of the six scenarios that trigger the initialization of a type, and the action in these six scenarios is called an active reference to a type. In addition, none of the reference types trigger initialization, called passive references.

For example, the following scenarios are passive references:

  • Reference by subclass to a static field of the parent class does not result in subclass initialization;

  • Referencing a class through an array definition does not trigger initialization of the class;

  • Constants are stored in the constant pool of the calling class at compile time. They are not directly referenced to the class that defines the constant, so they do not trigger initialization of the class that defines the constant.

2.2 Class loading process

loading

During the load phase, the Java virtual machine needs to do three things:

Gets the binary byte stream that defines a class by its fully qualified name.

Transform the static storage structure represented by this byte stream into the runtime data structure of the method area.

Generate a java.lang. Class object in memory that represents the Class and acts as an access point for the Class’s various data in the method area.

validation

Validation is the first step in the connection phase. The purpose of this phase is to ensure that the information contained in the byte stream of a Class file complies with all the constraints of the Java Virtual Machine Specification and that the information can be run as code without compromising the security of the virtual machine itself.

During the verification phase, the following four stages will be roughly completed:

  • File format validation The first phase verifies that the byte stream complies with the Class file format specification and can be processed by the current version of the VIRTUAL machine. The verification points mainly include: whether to start with magic number 0xCAFEBABE; Check whether the major and minor versions are within the processing range of the current VM. Whether the constants in the constant pool have unsupported constant types; Whether the parts of the Class file and the file itself have been deleted or additional information, etc.

  • The second stage of metadata verification is semantic analysis of the information described by bytecode to ensure that the information described conforms to the requirements of Java language specifications. The verification points in this stage include: whether the class has a parent class; Whether the parent of this class inherits classes that are not allowed to be inherited; If the class is not abstract, whether it implements all methods required by its parent class or interface; Class fields, methods that conflict with the parent class, and so on.

  • The third stage of bytecode verification is the most complicated one in the whole verification process. The main purpose is to determine the program semantics is legal and logical through data flow and control flow analysis.

  • Symbolic reference validation The final stage of symbolic reference validation occurs when the virtual machine converts symbolic references to direct references, which takes place during the parse phase, the third stage of the connection. Symbolic reference verification can be regarded as the matching verification of all kinds of information outside the class itself (various symbolic references in the constant pool). In general speaking, that is, whether the class is missing or prohibited from accessing some external classes, methods, fields and other resources on which it depends.

To prepare

The preparation phase is when memory is formally allocated and initial values are set for class variables.

parsing

The parsing phase is the process by which the virtual machine replaces symbolic references in the constant pool with direct references.

Initialize the

The class initialization stage is the last step in the class loading process. In the previous class loading process, except for the user application can participate in the loading stage through the custom class loader, the rest of the action is completely dominated and controlled by the virtual machine.

In the initialization phase, you actually start executing the Java program code defined in the class.

2.3 Class loaders

Although the class loader is only used to implement the loading action of the class, it plays a far more important role in Java programs than the class loading phase.

For any class, the uniqueness of the Java virtual machine must be established by both the classloader that loads it and the class itself. Each classloader has a separate class namespace.

This sentence can express more popular: compare whether the two classes “equal”, only in these two classes are from the same Class loader under the premise of load to be meaningful, otherwise, even if these two classes derived from the same Class files, by the same Java virtual machine loading, as long as the load they’re different Class loaders, these two classes are not necessarily equal.

Parental delegation model

From the perspective of Java virtual machines, there are only two different class loaders: one is the Bootstrap ClassLoader, which is implemented in C++ and is part of the virtual machine itself; The other is all the other class loaders, which are implemented in Java, independent of the virtual machine, and all inherit from the abstract java.lang.classloader class.

From a Java developer’s perspective, class loaders can be divided into:

  • Bootstrap ClassLoader: this ClassLoader is responsible for loading the libraries stored in the <java_home>\lib directory into the virtual machine memory. The startup class loader cannot be directly referenced by Java programs. When you write a custom class loader, if you need to delegate the loading request to the startup class loader, you can use NULL instead.

  • Extension ClassLoader: The classloader, implemented by sun.misc.Launcher$ExtClassLoader, is responsible for loading all libraries in the < java_HOME >\lib\ext directory, or in the path specified by the java.ext.dirs system variable. Developers can use the extended class loader directly;

  • Application ClassLoader: This ClassLoader is implemented by sun.misc.Launcher$AppClassLoader. The getSystemClassLoader() method returns this class loader, and is therefore also known as the system class loader. It is responsible for loading the libraries specified on the user’s ClassPath. Developers can use this class loader directly, and if the application does not have its own custom class loader, this is usually the default class loader in the application.

All of our applications are loaded by these three types of loaders in conjunction with each other, and we can also define our own class loaders if necessary. Their relationship is shown below:

The parent delegate model requires that all class loaders have their own parent class loaders, except for the top-level start class loaders.

The working process of the parental delegation model is:

  • If a classloader receives a classload request, it will not attempt to load the class itself in the first place

  • Instead, the request is delegated to the parent class loader, at every level of class loader

  • Therefore, all load requests should eventually be sent to the top layer of the boot class loader

  • Only when the parent loader reports that it cannot complete the load request (it did not find the desired class in its search scope) will the child loader attempt to complete the load itself.

The advantage of this is that Java classes have a hierarchy of priorities along with their classloaders. For example, java.lang. Object, which is stored in rt.jar, is delegated to the bootstrap class loader at the top of the model by whichever class loader loads the class, so Object is the same class in each class loader environment of the program.

Instead of using the parent delegate model and loading by class loaders, if a user writes a class called java.lang. Object and places it in the program’s ClassPath, multiple Object classes will appear. The most basic behavior in the Java type system is not guaranteed.

The parent delegate model is very important for the stable operation of Java programs, but it is surprisingly simple to implement, with only a dozen lines of code to implement the parent delegate, all in the loadClass() method of java.lang.classLoader:

protected synchronized Class<? > loadClass(String name, Boolean resolve) throws ClassNotFoundException {// First, check whether the requested Class has been loaded Class<? > c = findLoadedClass(name); if (c == null) { try { if (parent ! = null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); }} Catch (ClassNotFoundException e) {if (c == null) {// If (c == null) {// If (c == null) { C = findClass(name); } } if (resolve) { resolveClass(c); } return c; }Copy the code

03 JVM tuning Practice

3.1 JVM running Parameters

There are many parameters that can be set within the JVM to make it run efficiently in a variety of environments. Leave most of the parameters as default.

Three parameter types

  • Standard parameters

  • -help

  • -version

  • -x parameter (non-standard parameter)

  • -Xint

  • -Xcomp

  • XX parameter (high utilization rate)

  • -XX:newSize

  • -XX:+UseSerialGC

– X parameter

The JVM’s -x parameter is a nonstandard parameter, which may vary from JVM version to JVM version. You can view the nonstandard parameter using Java-x.

XX parameters –

The -xx parameter is also a non-standard parameter and is used for JVM tuning and debug operations.

The -xx parameter can be used in two ways: Boolean and non-Boolean:

  • Boolean type

  • Format: -xx :[+-] Indicates whether to enable or disable an attribute

  • For example, -xx :+DisableExplicitGC disables manual invocation of THE GC operation, that is, calling System.gc() is invalid

  • The Boolean type

  • Format: -xx := Indicates that the value of the attribute is

  • For example, -xx :NewRatio=4 indicates that the ratio of Cenozoic to old age is 1:4

-xms and -xmx parameters

-xms and -xmx set the initial size and maximum size of the JVM’s heap memory, respectively. -xmx2048m: equivalent to -xx :MaxHeapSize, sets the maximum heap memory of the JVM to 2048M. -xMS512m: Equivalent to -xx :InitialHeapSize, sets the initial HEAP memory of the JVM to 512M. Properly resizing the MEMORY of the JVM can take advantage of server resources and make your programs run faster. Example:

[root@node01 test]# java -Xms512m -Xmx2048m TestJVM
itcast
Copy the code

jstat

The jstat command shows how much of the heap memory is being used, as well as how many classes are being loaded. The command format is as follows: jstat [- command options] [vmid] [Interval/millisecond] [Number of queries]

View class load statistics

F:\t>jstat -class Unloaded Bytes Unloaded Bytes Time 5962 10814.2 0 0.0 3.75Copy the code

Bytes: the number of Unloaded classes Bytes: the number of Unloaded Spaces Time: the Time when the Unloaded space is Unloaded

Viewing compiled statistics

F:\ T >jstat - Compiler 12076 Compiled Failed Invalid Time FailedType FailedMethod 3115 0 0 3.43 0Copy the code

Compiled: indicates the Compiled number. Failed: Failed quantity Invalid: unavailable quantity Time: Time FailedType: failure type FailedMethod: Failed method

Garbage Collection statistics

F:\ T >jstat -GC 12076 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062 # Print five times F:\ T >jstat -GC 12076 1000 5 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062 3584.0 6656.0 3412.1 0.0 180224.0 89915.4 61440.0 5332.1 27904.0 2626 7.3 3840.0 3420.8 6 0.036 1 0.026 0.062Copy the code

S0C: size of the first Survivor zone (KB) S1C: size of the second Survivor zone (KB) S0U: size of the first Survivor zone (KB) S1U: size of the second Survivor zone (KB) EC: EU: Eden space (KB) OC: Old space (KB) OU: Old space (KB) MC: method space (KB) MU: Method space (KB) CCSC: compressed class space (KB) CCSU: Compressed class space (KB) Compressed space usage (KB) YGC: garbage collection count of the young generation YGCT: garbage collection count of the young generation FGC: garbage collection count of the old age FGCT: garbage collection count of the old age GCT: garbage collection count

3.2 Jmap usage and memory overflow analysis

While jstat provides statistical analysis of JVM heap memory, JMap provides more detailed information, such as summary of memory usage, location and analysis of memory leaks.

Check memory usage

[root@node01 ~]# jmap -heap 6219 Attaching to process ID 6219, please wait... Debugger attached successfully. Server Compiler detected. JVM version is 25.141-b15 using ththread -local object allocation. Parallel GC with 2 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 488636416 (466.0MB) NewSize = 10485760 (10.0MB) MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 488636416 (466.0MB) MaxNewSize = 162529280 (155.0MB) OldSize = 20971520 (20.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: # Heap Usage PS Young Generation # Heap Usage: Capacity = 123731968 (118.0MB) Used = 1384736 (1.320587158203125MB) Free = 122347232 (116.67941284179688MB) 1.1191416594941737%, informs the From Space: Capacity = 9437184 (9.0MB) Used = 0 (0.0MB) Free = 9437184 (9.0MB) 0.0% used To Space: Capacity = 9437184 (9.0MB) Used = 0 (0.0MB) Free = 9437184 (9.0MB) 0.0% Used PS Old Generation # Capacity = 28311552 (27.0MB) Used = 13698672 (13.064071655273438MB) Free = 14612880 (13.935928344726562MB) 48.38545057508681% Used interned Strings occupying 1866368 bytes.Copy the code

View the number and size of objects in memory

# view all objects, Including active and inactive jmap histo - < pid > | more # to check the active object jmap - histo: live < pid > | more/root @ node01 ~ # jmap - histo: live | 6219 more num #instances #bytes class name ----------------------------------------------1: 37437 7914608 [C 2: 34916 837984 java.lang.String 3: 884 654848 [B 4: 17188 550016 java.util.HashMap$Node 5: 3674 424968 java.lang.Class 6: 6322 395512 [Ljava.lang.Object; 7: 3738 328944 java.lang.reflect.Method 8: 1028 208048 [Ljava.util.HashMap$Node; 9: 2247 144264 [I 10: 4305 137760 java.util.concurrent.ConcurrentHashMap$Node 11: 1270 109080 [Ljava.lang.String; 12: 64 84128 [Ljava.util.concurrent.ConcurrentHashMap$Node; 13: 1714 82272 java.util.HashMap 14: 3285 70072 [Ljava.lang.Class; 15: 2888 69312 java.util.ArrayList 16: 3983 63728 java.lang.Object 17: 1271 61008 org.apache.tomcat.util.digester.CallMethodRule 18: 1518 60720 java.util.LinkedHashMap$Entry 19: 1671 53472 com.sun.org.apache.xerces.internal.xni.QName 20: 88 50880 [Ljava.util.WeakHashMap$Entry; 21: 618 49440 java.lang.reflect.Constructor 22: 1545 49440 java.util.Hashtable$Entry 23: 1027 41080 java.util.TreeMap$Entry 24: 846 40608 org.apache.tomcat.util.modeler.AttributeInfo 25: 142 38032 [S 26: 946 37840 java.lang.ref.SoftReference 27: B byte C char D double F float I int J long Z Boolean For example, [I denotes int[] [L+ class name other objectCopy the code

Dump memory usage to a file

Jmap -dump:format=b,file=dumpFileName <pid> # example jmap -dump:format=b,file=/ TMP /dump.dat 6219Copy the code

You can see the dump.dat file generated under/TMP.

Dump files are analyzed by jHAT

In the previous section, we dumped the MEMORY of the JVM into a file. This file is a binary file that is not easy to view, so we can use the Jhat tool to view it.

[root@node01 TMP]# jhat-port 9999 / TMP /dump.dat Reading from/TMP /dump.dat... Dump file created Mon Sep 10 01:04:21 CST 2018 Snapshot read, resolving... Resolving 204094 objects... Chasing references, expect 40 dots........................................ Eliminating duplicate references........................................ Snapshot resolved. Started HTTP server on port 9999 Server is ready.Copy the code

Open a browser to visit: http://192.168.40.133:9999/

At last by OQL query function

3.3 Jmp usage and memory overflow analysis

Use MAT to locate and analyze memory overflow

Memory overruns are common in real production environments, such as constantly writing data to a collection, having an infinite loop, reading large files, and so on.

If memory overflow occurs, first of all, we need to locate the link where memory overflow occurs, and analyze whether it is normal or abnormal. If it is normal, we should consider increasing the memory setting. If it is abnormal, we should modify the code to fix the bug. First, we need to learn how to locate the problem and then analyze it. How to locate the problem, we need to use jMAP and MAT tools for location analysis.

Next, we simulate the memory overflow scenario.

Simulated memory overflow

Write code to add 1 million strings, each consisting of 1000 UUID, to the List collection. If the program works, finally print OK.

public class TestJvmOutOfMemory { public static void main(String[] args) { List<Object> list = new ArrayList<>(); for (int i = 0; i < 10000000; i++) { String str = ""; for (int j = 0; j < 1000; j++) { str += UUID.randomUUID().toString(); } list.add(str); } System.out.println("ok"); }}Copy the code

To demonstrate the effect, we will set the execution parameters

# parameters are as follows: - Xms8m Xmx8m - XX: + HeapDumpOnOutOfMemoryErrorCopy the code

Run the test

java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid5348.hprof ... Heap dump file created [8137186 bytes in 0.032 secs] the Exception in the thread "is the main" Java. Lang. OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448) at java.lang.StringBuilder.append(StringBuilder.java:136) at cn.itcast.jvm.TestJvmOutOfMemory.main(TestJvmOutOfMemory.java:14) Process finished with exit code 1Copy the code

As you can see, the file is dumped to javA_pid5348.hprof when a memory overflow occurs.

Import it to MA T tool for analysis

As you can see, 91.03% of memory is occupied by the Object[] array, so it is suspicious. Analysis: This suspect is correct, because more than 90% of the memory is occupied by it, which is very likely to overflow memory.

You can see that the collection stores a large number of UUID strings

3.4 Use of Jsatck

When we need to check the JVM thread execution, for example, the server CPU load suddenly increased, deadlock, deadlock, etc. How do we analyze?

Since the program is running without any output, and the logs show nothing wrong, you need to look at the JVM’s internal threads and analyze them to find the cause.

The jstack command takes a snapshot of the running JVM’s threads and prints it:

#用法:jstack <pid>
[root@node01 bin]# jstack 2203
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.141-b15 mixed mode):
"Attach Listener" #24 daemon prio=9 os_prio=0 tid=0x00007fabb4001000 nid=0x906 waiting on
condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"http-bio-8080-exec-5" #23 daemon prio=5 os_prio=0 tid=0x00007fabb057c000 nid=0x8e1
waiting on condition [0x00007fabd05b8000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000f8508360> (a
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) 
at
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueue
dSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-4" #22 daemon prio=5 os_prio=0 tid=0x00007fab9c113800 nid=0x8e0
waiting on condition [0x00007fabd06b9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000f8508360> (a
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueue
dSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-3" #21 daemon prio=5 os_prio=0 tid=0x0000000001aeb800 nid=0x8df
waiting on condition [0x00007fabd09ba000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000f8508360> (a
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueue
dSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) 
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-2" #20 daemon prio=5 os_prio=0 tid=0x0000000001aea000 nid=0x8de
waiting on condition [0x00007fabd0abb000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000f8508360> (a
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueue
dSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-1" #19 daemon prio=5 os_prio=0 tid=0x0000000001ae8800 nid=0x8dd
waiting on condition [0x00007fabd0bbc000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000f8508360> (a
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueue
dSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"ajp-bio-8009-AsyncTimeout" #17 daemon prio=5 os_prio=0 tid=0x00007fabe8128000 nid=0x8d0
waiting on condition [0x00007fabd0ece000]
java.lang.Thread.State: TIMED_WAITING (sleeping) 
at java.lang.Thread.sleep(Native Method)
at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:152)
at java.lang.Thread.run(Thread.java:748)
"ajp-bio-8009-Acceptor-0" #16 daemon prio=5 os_prio=0 tid=0x00007fabe82d4000 nid=0x8cf
runnable [0x00007fabd0fcf000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at
org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFac
tory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-AsyncTimeout" #15 daemon prio=5 os_prio=0 tid=0x00007fabe82d1800 nid=0x8ce
waiting on condition [0x00007fabd10d0000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:152)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-Acceptor-0" #14 daemon prio=5 os_prio=0 tid=0x00007fabe82d0000 nid=0x8cd
runnable [0x00007fabd11d1000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at
org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFac
tory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:748)
"ContainerBackgroundProcessor[StandardEngine[Catalina]]" #13 daemon prio=5 os_prio=0
tid=0x00007fabe82ce000 nid=0x8cc waiting on condition [0x00007fabd12d2000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at
org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.jav
a:1513)
at java.lang.Thread.run(Thread.java:748)
"GC Daemon" #10 daemon prio=2 os_prio=0 tid=0x00007fabe83b4000 nid=0x8b3 in Object.wait()
[0x00007fabd1c2f000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e315c2d0> (a sun.misc.GC$LatencyLock)
at sun.misc.GC$Daemon.run(GC.java:117)
- locked <0x00000000e315c2d0> (a sun.misc.GC$LatencyLock) 
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fabe80c3800 nid=0x8a5 runnable
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fabe80b6800 nid=0x8a4 waiting
on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fabe80b3800 nid=0x8a3 waiting
on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fabe80b2000 nid=0x8a2 runnable
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fabe807f000 nid=0x8a1 in Object.wait()
[0x00007fabd2a67000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3162918> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000e3162918> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fabe807a800 nid=0x8a0 in
Object.wait() [0x00007fabd2b68000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3162958> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e3162958> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x00007fabe8009000 nid=0x89c runnable [0x00007fabed210000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:453)
at org.apache.catalina.startup.Catalina.await(Catalina.java:777)
at org.apache.catalina.startup.Catalina.start(Catalina.java:723)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:321)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:455) 
"VM Thread" os_prio=0 tid=0x00007fabe8073000 nid=0x89f runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fabe801e000 nid=0x89d runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fabe8020000 nid=0x89e runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007fabe80d6800 nid=0x8a6 waiting on condition
JNI global references: 43
Copy the code

3.5 Use of VisualVM tools

VisualVM, which can monitor threads, memory, view methods’ CPU time and memory objects, objects that have been GC, and see the allocated stack in reverse (e.g., by which 100 Strings are allocated).

VisualVM is easy to use, almost zero configuration, or relatively rich in features, almost all of the functions of other JDK built-in commands.

  • Memory information

  • Thread information

  • Dump heap (local process

  • Dump thread (local process)

  • Open heap Dump. Heap dumps can be generated using Jmap

  • Open thread Dump

  • Generate a snapshot of the application (including memory information, thread information, and so on)

  • Performance analysis. CPU analysis (call time of each method, check which method takes more time), memory analysis (memory occupied by various objects, check which classes occupy more memory)

  • .

Start the

In the bin directory of the JDK installation directory, find jVisualvm. exe and double-click to open it.

View CPU, memory, class, and thread running information

See thread information

You can also click the Dump button in the upper right corner to export the thread information, which is the jStack command executed.

Monitoring remote JVMS

The VisualJVM can monitor not only local JVM processes, but also remote JVM processes with the help of JMX technology.

What is a JMX

JMX (Java Management Extensions) is a framework for implementing Management capabilities for applications, devices, systems, and so on. JMX provides the flexibility to develop seamlessly integrated systems, networks, and service management applications across a range of heterogeneous operating system platforms, system architectures, and network transport protocols.

Monitoring Tomcat

To monitor remote Tomcat, you need to configure JMX in remote Tomcat as follows:

# change catalina.sh in tomcat bin Add the following parameters JAVA_OPTS = "- Dcom. Sun. Management. Jmxremote - Dcom. Sun. Management jmxremote. Port = 9999 - Dcom. Sun. Management. Jmxremote. Authenticate = false - Dcom. Sun. Management jmxremote. SSL = false "# this several parameters are: # - Dcom. Sun. Management. Jmxremote: allows you to use the JMX remote management # - Dcom. Sun. Management jmxremote. Port = 9999: The JMX remote connection port # - Dcom. Sun. Management jmxremote. Authenticate = false: No authentication, any user can connect # - Dcom. Sun. Management jmxremote. SSL = false: do not use SSLCopy the code

Remotely connect to Tomcat using the VisualJVM

Add the host

There may be many JVMS that need to be monitored on a single host, so the next step is to add JVMS that need to be monitored on that host:

The connection succeeded. Using the same method as before, you can monitor remote Tomcat processes just as you can monitor local JVM processes.

3.6 Visual GC log analysis tools

GC log output parameters

-xx :+PrintGCDetails can be used to print the GC log, which can be viewed on the console. Although the GC information can be viewed, it is not intuitive. You can use a third-party GC log analysis tool to view it.

Parameters involved in log output are as follows:

-xx :+PrintGC prints GC logs -xx :+PrintGCDetails Prints GC logs -xx :+PrintGCTimeStamps Prints GC timestamps (in the form of baseline time) -xx :+PrintGCDateStamps -xx :+PrintHeapAtGC Prints heap information before and after GC -Xloggc:.. /logs/gc.log Output path of the log fileCopy the code

Testing:

-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m -XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
-Xloggc:F://test//gc.log
Copy the code

After you run it, you can generate the gC.log file on drive E.

Use GC Easy

It is an online visualization tool, easy to use, powerful, website: gceasy.io/

3.7 Tuning actual combat

Start by deploying a Web project (prepare yourself)

Pressure test

The following stress test was conducted by JMeter, which first measured the amount of concurrency in the initial state and other information. Then we tuned the JVM and compared it with the data measured in the initial state to see whether the tuning was good or bad.

First, you need to adjust the parameters of JMeter. The default memory size of JMeter is only 1g. If the number of concurrent requests exceeds 300, the system will not be able to run normally and will throw exceptions such as memory overflow, so you need to adjust the memory size. Modify jmeter.bat file: Set HEAP= -xms1g -XMx4g -xx :MaxMetaspaceSize=512m Jmeter Defaults to '-xx :+ useg1gC-xx :MaxGCPauseMillis=100 -xx :G1ReservePercent=20'Copy the code

Add GC parameters

The memory Settings are smaller for more frequent GC, so that the effect can be observed. JAVA_OPTS=" -xx :+UseParallelGC -xx :+UseParallelOldGC -xMS64m -XMx128m -xx :+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC - Xloggc:.. /logs/gc.log -Dcom.sun.management.jmxremote - Dcom.sun.management.jmxremote.port=9999 - Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false"Copy the code

Restart the tomcat

Create test cases for pressure measurement

The tuning direction is mainly from the following aspects:

  • Adjust the memory

  • Replace garbage collector

Here are some tips for tuning the JVM:

  • The JVM in the production environment must be set to parameters, not all production by default.

  • For parameter setting, do not beat your head off, need to come to a conclusion through actual concurrency or stress test.

  • In the case of temporary objects in memory, make the young generation larger. If it is G1 or ZGC, no setting is required.

  • Carefully analyze the report given by GCEASY, analyze the causes and identify the problems.

  • The G1 or ZGC garbage collector is recommended for low-latency applications.

  • Do not focus solely on JVM parameters. There are many factors that affect performance, such as operating system parameters, tomcat parameters, etc.

PerfMa

PerfMa provides JVM parameter analysis, thread analysis, and heap memory analysis with a beautiful and powerful interface that can be used as an auxiliary tool when doing JVM tuning. Website: www.perfma.com/

04 Tomcat8 optimization

4.1 Disabling AJP Connections

On the service status page, you can see that the AJP service is enabled by default and occupies port 8009.

What is AJP?

The Apache JServer Protocol (AJP) AJPv13 Protocol is package-oriented. The WEB server and Servlet container interact over a TCP connection; To save on costly SOCKET creation, the WEB server tries to maintain a permanent TCP connection to the servlet container and reuse the connection over multiple request and response cycles.

We generally use the Nginx+ Tomcat architecture, so we don’t need the AJP protocol, so we disable the AJP connector.

Modify the server. XML file in conf to disable the AJP service.

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Copy the code

4.2 Executor (Thread Pool)

Each user request is a thread in Tomcat, so you can use thread pools to improve performance.

Modify the server.xml file:

<! <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="50" prestartminSpareThreads="true" maxQueueSize="100"/> <! MaxThreads: Specifies the maximum number of concurrent threads. The default value is 200. The recommended value ranges from 500 to 1000 based on hardware facilities and services. PrestartminSpareThreads Specifies the number of threads created during Tomcat initialization. The default value is 25. MaxQueueSize specifies the maximum number of threads that can be queued before requests are rejected --> <! <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />Copy the code

Save the Settings and exit. Restart Tomcat to check the result.

4.3 Three operating modes

There are three operating modes of Tomcat:

\1. Bio default mode, very low performance, without any optimization process and support.

Nio NIO (New I/O) is a new way of doing I/O operations (i.e., the java.nio package and its subpackages) provided by Java SE 1.4 and later. Java NIO is a buffer-based Java API that provides non-blocking I/O operations, so NIO is also known as an acronym for non-blocking I/O. It has better concurrency performance than traditional I/O operations (BIO).

\3. Apr is the most difficult to install, but it solves the problem of asynchronous IO at the operating system level, greatly improving performance.

Nio is recommended, however, there is the latest niO2 in Tomcat8, which is faster, and niO2 is recommended.

Set the nio2

<Connector executor="tomcatThreadPool" port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol" connectionTimeout="20000" redirectPort="8443" />
Copy the code

05 Code optimization Suggestions

(1) Use local variables as much as possible

Parameters passed when a method is called, as well as temporary variables created during the call, are quickly stored in the stack, while other variables, such as static variables, instance variables, and so on, are created in the heap and are slower. In addition, variables created in the stack are removed as the method completes its run, requiring no additional garbage collection.

(2) Minimize the repeated calculation of variables

To be clear, a call to a method, even if it contains only one statement, is costly. So for example:

for (int i = 0; i < list.size(); i++) {... }Copy the code

You are advised to replace it with:

int length = list.size(); for (int i = 0, i < length; i++) {... }Copy the code

This saves a lot of overhead when list.size() is large.

(3) Try to use the lazy loading strategy, that is, create when needed

String str = "aaa"; if (i == 1){ list.add(str); }// If (I == 1){String STR = "aaa"; list.add(str); }Copy the code

(4) Exceptions should not be used to control the flow

Exceptions are detrimental to performance. To throw an exception, a new object is created. The constructor of the Throwable interface calls a local synchronization method called fifillInStackTrace(), which checks the stack to collect call trace information. Whenever an exception is thrown, the Java virtual machine must adjust the call stack because a new object is created during processing. Exceptions should only be used for error handling and should not be used to control program flow.

(5) Do not declare arrays as public static final

Declaring an array public is a security loophole, which means that the array can be changed by an external class.

(6) Do not create some unused objects, do not import some unused classes

This makes no sense. If “The value of The local variable I is not used”, “The import java.util is never used” appear in The code, delete these useless contents

(7) Avoid using reflection during program operation

Reflection is a powerful feature that Java provides to users, and powerful features often mean inefficient. It is not recommended to use invoke methods that use reflection frequently, especially Method, during program execution.

If necessary, it is recommended that classes that need to be loaded by reflection instantiate an object by reflection and put into memory at project startup.

(8) Use database connection pool and thread pool

Both pools are designed to reuse objects, the former to avoid frequent opening and closing of connections, and the latter to avoid frequent thread creation and destruction.

(9) When the container is initialized, specify the length as much as possible

When the container is initialized, specify the length as possible, for example: new ArrayList<>(10); new HashMap<>(32); Avoid performance loss caused by capacity expansion when the container length is insufficient.

(10) ArrayList is fast to randomly traverse, LinkedList is fast to add and delete

(11) Use Entry to traverse the map

(12) Do not manually call System().gc;

(13) String use regular expressions as little as possible

Regular expressions are powerful, but inefficient. Use them sparingly unless necessary.

Replace () does not support re. ReplaceAll () supports re

Replace () is recommended for characters only.

(14) Log output should pay attention to the level

It is recommended that close() of resources be operated separately