调优分类说明

jvm调优主要分为两个方面,代码调优和GC调优。
无论哪种调优,使用top命令查看当前内存和CPU使用情况是否存在问题

[root@iZ8vb5a7qagk5piviztthoZ java]# top

检查是否有异常的%CPU 和%MEM占用

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                        
904 root      20   0  425268  31168  16400 S   0.3   1.7   0:00.30 tuned        

可以看到,我的系统里只有一个mysql占用较高,但也仅为1.7%的内存占用

代码调优

代码调优是根据服务器的运行状态判断代码是否存在可优化的部分。
可优化的部分大概分为死锁、OOM等问题,可从以下几个方面入手进行诊断修正

jps命令可以查看所有正在执行的java进程端口号

[root@iZ8vb5a7qagk5piviztthoZ ~]# jps
1736 Jps
1627 jar

服务器上只有一个jar包在执行,端口号是1627

使用jinfo可以查看jar包启动时对应的参数,包含手动设置的参数和默认参数

 jinfo -flags 1627

从输出中可以看到jvm的版本号以及一系列的启动参数(Non-default VM flags),jdk1.8在没有设置的情况下默认使用的GC是parallel+parallel old

Attaching to process ID 1627, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.281-b09
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=29360128 -XX:MaxHeapSize=459276288 -XX:MaxNewSize=153092096 -XX:MinHeapDeltaBytes=196608 -XX:NewSize=9764864 -XX:OldSize=19595264 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps 
Command line:

1. CPU占用过高

如果CPU有异常情况则可以进一步使用 top -p <端口号> 查看具体的内存和CPU占用

top -p 1627

执行命令会跳转到如下界面

top - 13:33:36 up 49 min,  2 users,  load average: 0.00, 0.00, 0.00
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem :   1745.4 total,    191.5 free,    708.6 used,    845.2 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.    890.4 avail Mem 

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                        
1627 root      20   0 2396604 121232  16340 S   0.0   6.8   0:07.18 java 

然后按住大写的H查看进程下每个线程的内存和CPU使用情况,输出如下


PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                        
1627 root      20   0 2396604 121232  16340 S   0.0   6.8   0:07.26 java                                                           
top - 13:37:08 up 52 min,  2 users,  load average: 0.00, 0.00, 0.00
Threads:  28 total,   0 running,  28 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
MiB Mem :   1745.4 total,    191.9 free,    708.2 used,    845.3 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.    890.8 avail Mem 

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                        
1627 root      20   0 2396604 121232  16340 S   0.0   6.8   0:00.00 java                                                           
1628 root      20   0 2396604 121232  16340 S   0.0   6.8   0:01.96 java                                                           
1629 root      20   0 2396604 121232  16340 S   0.0   6.8   0:00.30 java                                                           
1630 root      20   0 2396604 121232  16340 S   0.0   6.8   0:00.00 java                                                           
1631 root      20   0 2396604 121232  16340 S   0.0   6.8   0:00.00 java 

找到内存占用最高的线程的pid转换为16进制的tid,例如将1628转换为65c得到0x65c
最后用jstack得到线程堆栈信息中 65b 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调用方法

jstack 1627|grep -A 10 65c

该命令为查看进程号为1627下的线程号为1628的线程信息,输出如下

"DestroyJavaVM" #27 prio=5 os_prio=0 tid=0x00007f5a5800a800 nid=0x65c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-2" #26 prio=5 os_prio=0 tid=0x00007f5a589ba800 nid=0x692 waiting on condition [0x00007f5a295bc000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.example.demo.DemoApplication$1.run(DemoApplication.java:19)
        at java.lang.Thread.run(Thread.java:748)

"http-nio-8080-Acceptor" #25 daemon prio=5 os_prio=0 tid=0x00007f5a5899b000 nid=0x691 runnable [0x00007f5a298bd000]
   java.lang.Thread.State: RUNNABLE
基于此可以根据线程信息进行问题的代码定位

2. 内存占用过高

jmap命令用来查看进程内存信息,实例个数以及占用内存大小

[root@iZ8vb5a7qagk5piviztthoZ ~]jmap -histo 1627 >/java/log.txt 

日志文件将会被输出到/java/目录下的log.txt文件中,文件内容如下

 num     #instances         #bytes  class name
----------------------------------------------
   1:         31296        3094848  [C
   2:          5656        1702416  [I
   3:          3286         769520  [B
   4:         30328         727872  java.lang.String
   5:          7967         701096  java.lang.reflect.Method
   6:          5920         654416  java.lang.Class
   7:         18707         598624  java.util.concurrent.ConcurrentHashMap$Node
   8:          6757         384992  [Ljava.lang.Object;
   9:          7355         294200  java.util.LinkedHashMap$Entry
  10:          3298         279664  [Ljava.util.HashMap$Node;
  11:           110         237408  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  12:          6281         200992  java.util.HashMap$Node
  13:         11832         189312  java.lang.Object
  14:          3183         178248  java.util.LinkedHashMap
  15:          7543         170280  [Ljava.lang.Class;
  16:          1602         115344  java.lang.reflect.Field
  17:           811          97320  org.springframework.boot.loader.jar.JarEntry
  18:          1196          96192  [Ljava.util.WeakHashMap$Entry;
  19:          1994          95712  org.springframework.util.ConcurrentReferenceHashMap$SoftEntryReference
  20:          1668          80064  java.util.HashMap
  21:           908          72640  java.lang.reflect.Constructor
  22:          1235          69160  java.lang.invoke.MemberName
  23:          1316          63168  org.springframework.core.ResolvableType
  24:          1815          58080  java.lang.ref.ReferenceQueue
  25:           926          55480  [Ljava.lang.reflect.Method;
  26:          1276          51040  java.lang.ref.SoftReference
  27:           890          49840  java.lang.Class$ReflectionData
  28:           778          49792  org.springframework.boot.loader.jar.JarFileWrapper
  29:          1202          48080  java.lang.ref.Finalizer
  30:          1008          44992  [Ljava.lang.String;
  31:          1096          43840  java.lang.invoke.MethodType

其中
num:序号
instances:实例数量
bytes:占用空间大小
class name:类名称
[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]

由于我在测试服务器只跑了一个计数器的测试程序,所以不存在高内存占用情况,打印出来的都是springboot内部的类,如果有高内存占用则可以看出是哪个类占用了大量的内存

也可以用jmap -dump命令导出文件后用windows下的可视化工具jvisualvm进行分析,查看内存占用较高的类

[root@iZ8vb5a7qagk5piviztthoZ java]#jmap -dump:format=b,file=java_pargram.hprof 1627
Dumping heap to /java/java_pargram.hprof ...
Heap dump file created

jmap还有一个参数 heap可以打印进程号对应的应用的堆信息,查看实时的堆数据

jmap -heap 1627

打印出的堆信息详细的描述了堆的当前利用情况

Attaching to process ID 1627, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.281-b09

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 459276288 (438.0MB)
   NewSize                  = 9764864 (9.3125MB)
   MaxNewSize               = 153092096 (146.0MB)
   OldSize                  = 19595264 (18.6875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 8847360 (8.4375MB)
   used     = 2021016 (1.9273910522460938MB)
   free     = 6826344 (6.510108947753906MB)
   22.843153211805557% used
Eden Space:
   capacity = 7864320 (7.5MB)
   used     = 1289280 (1.22955322265625MB)
   free     = 6575040 (6.27044677734375MB)
   16.39404296875% used
From Space:
   capacity = 983040 (0.9375MB)
   used     = 731736 (0.6978378295898438MB)
   free     = 251304 (0.23966217041015625MB)
   74.43603515625% used
To Space:
   capacity = 983040 (0.9375MB)
   used     = 0 (0.0MB)
   free     = 983040 (0.9375MB)
   0.0% used
tenured generation:
   capacity = 19595264 (18.6875MB)
   used     = 11137984 (10.62200927734375MB)
   free     = 8457280 (8.06549072265625MB)
   56.84018342391305% used

3. 死锁

用jstack加进程id查找死锁

 jstack 1627

该命令会打印出所有的线程信息

2021-02-13 13:27:07
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.281-b09 mixed mode):

"Attach Listener" #28 daemon prio=9 os_prio=0 tid=0x00007f5a34002000 nid=0x6fd waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #27 prio=5 os_prio=0 tid=0x00007f5a5800a800 nid=0x65c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-2" #26 prio=5 os_prio=0 tid=0x00007f5a589ba800 nid=0x692 waiting on condition [0x00007f5a295bc000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.example.demo.DemoApplication$1.run(DemoApplication.java:19)
        at java.lang.Thread.run(Thread.java:748)

"http-nio-8080-Acceptor" #25 daemon prio=5 os_prio=0 tid=0x00007f5a5899b000 nid=0x691 runnable [0x00007f5a298bd000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:424)
        at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:252)
        - locked <0x00000000e51861a0> (a java.lang.Object)
        at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:469)
        at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:71)
        at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:106)
        at java.lang.Thread.run(Thread.java:748)

"http-nio-8080-ClientPoller" #24 daemon prio=5 os_prio=0 tid=0x00007f5a5897b800 nid=0x690 runnable [0x00007f5a299be000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000e51863d8> (a sun.nio.ch.Util$3)
        - locked <0x00000000e51863e8> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000e5186390> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:711)
        at java.lang.Thread.run(Thread.java:748)

"http-nio-8080-exec-10" #23 daemon prio=5 os_prio=0 tid=0x00007f5a5893f000 nid=0x68f waiting on condition [0x00007f5a29abf000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-9" #22 daemon prio=5 os_prio=0 tid=0x00007f5a5893d000 nid=0x68e waiting on condition [0x00007f5a29bc0000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-8" #21 daemon prio=5 os_prio=0 tid=0x00007f5a5893b800 nid=0x68d waiting on condition [0x00007f5a29cc1000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-7" #20 daemon prio=5 os_prio=0 tid=0x00007f5a584da800 nid=0x68c waiting on condition [0x00007f5a29dc2000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-6" #19 daemon prio=5 os_prio=0 tid=0x00007f5a584d9000 nid=0x68b waiting on condition [0x00007f5a29ec3000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-5" #18 daemon prio=5 os_prio=0 tid=0x00007f5a584d7000 nid=0x68a waiting on condition [0x00007f5a29fc4000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-4" #17 daemon prio=5 os_prio=0 tid=0x00007f5a584d5800 nid=0x689 waiting on condition [0x00007f5a2a0c5000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-3" #16 daemon prio=5 os_prio=0 tid=0x00007f5a58573000 nid=0x688 waiting on condition [0x00007f5a2a1c6000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-2" #15 daemon prio=5 os_prio=0 tid=0x00007f5a58571800 nid=0x687 waiting on condition [0x00007f5a2a2c7000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-exec-1" #14 daemon prio=5 os_prio=0 tid=0x00007f5a58570000 nid=0x686 waiting on condition [0x00007f5a2a3c8000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000e51865a8> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:108)
        at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
        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-nio-8080-BlockPoller" #13 daemon prio=5 os_prio=0 tid=0x00007f5a583a5000 nid=0x685 runnable [0x00007f5a2a4c9000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
        at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
        at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
        at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
        - locked <0x00000000e51879d0> (a sun.nio.ch.Util$3)
        - locked <0x00000000e51879e0> (a java.util.Collections$UnmodifiableSet)
        - locked <0x00000000e5187988> (a sun.nio.ch.EPollSelectorImpl)
        at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
        at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:313)

"container-0" #12 prio=5 os_prio=0 tid=0x00007f5a588f7800 nid=0x681 waiting on condition [0x00007f5a2a7ca000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:570)
        at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:197)

"Catalina-utility-2" #11 prio=1 os_prio=0 tid=0x00007f5a58918800 nid=0x680 waiting on condition [0x00007f5a2a8cb000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000ee3f8748> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        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)

"Catalina-utility-1" #10 prio=1 os_prio=0 tid=0x00007f5a58912000 nid=0x67f waiting on condition [0x00007f5a4812c000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000ee3f8748> (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(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        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)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f5a580e1800 nid=0x663 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f5a580de800 nid=0x662 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f5a580dc800 nid=0x661 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

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

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f5a580a4800 nid=0x65f in Object.wait() [0x00007f5a48c20000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000edcb7520> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000edcb7520> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

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

"VM Thread" os_prio=0 tid=0x00007f5a58096800 nid=0x65d runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f5a580e4800 nid=0x664 waiting on condition 

JNI global references: 1227
"Thread-1" 线程名
prio=5 优先级=5
tid=0x000000001fa9e000 线程id
nid=0x2d64 线程对应的本地线程标识nid
java.lang.Thread.State: BLOCKED 线程状态

如果存在死锁的话在信息中会有如下的相应提示
image.png
提示中会详细说明死锁的位置和原因

GC调优

使用jstat查看堆内存各部分的使用量
jstat [-命令选项] [vmid] [间隔时间(毫秒)] [查询次数]
垃圾回收统计的使用方法
jstat -gc <pid>

jstat -gc 1627

打印内容如下:

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
960.0  960.0  714.6   0.0    7680.0   2923.2   19136.0    10876.9   30720.0 28678.1 4096.0 3661.3     34    0.102   1      0.021    0.123
S0C:第一个幸存区的大小,单位KB
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小(元空间)
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s

JVM运行状态评估
年轻代对象增长的速率可以执行命令

jstat -gc 1627 1000 10 (每隔1秒执行1次命令,共执行10次)

年轻代对象增长的速率
通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。

Young GC的触发频率和每次耗时
通过运行时间/Young GC的触发总数和YGCT/YGC两个公式可以算出young GC的触发频率和每次耗时。
通过这两个参数可以知道年轻代对象增长速率,根据结果我们大概就能知道系统大概多久会因为Young GC的执行而卡顿多久。

每次Young GC后有多少对象存活和进入老年代
这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率

Full GC的触发频率和每次耗时
知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式FGCT/FGC 计算得出。

得出以上结论后,以尽量减少full gc,尽量让对象在young gc的时候被干掉的原则,根据内存模型调整eden区和old的大小比例来进行jvm优化
image.png

ps:还可以通过打印GC日志的方式查看分析GC,进行问题定位
GC日志打印参数
对应的参数列表
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

Roy
13 声望1 粉丝

实用主义践行者,所学为所用,所用即所学。