JVM及Java监控原理与实践

JVM及Java监控原理与实践

简介

主要是介绍一下对运行Java程序的一些跟踪,以及对JVM内存等方面进行运维的一些方法。

反解析class文件的工具使用

  • 一般使用jd-gui工具进行反编译class文件
  • 有些jd-gui无法反编译的class,可以使用luyten工具进行反编译

Java进程分析命令介绍

JPS
用于获取所有的JVM进程信息,类似于linux的ps命令

1
2
3
4
5
6
7
8
//输出进程号与执行的主类名(jar包名)
jps -l

结果如下:

30352
20708 sun.tools.jps.Jps
6876 mtex-config-0.0.1-SNAPSHOT.jar

jstat
用于监控java进程内部各类运行状态信息的工具,可以监控本地或远程虚拟机类装载、内存、垃圾回收等信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//监控GC情况 间隔1000毫秒执行1次 执行5次
jstat -gc 6876 1000 5

执行结果如下:

C:\Users\dell>jstat -gc 6876 1000 5
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
13824.0 15872.0 13804.6 0.0 140288.0 129170.6 102400.0 19730.9 54528.0 52078.9 7168.0 6684.3 10 0.143 2 0.172 0.315
13824.0 15872.0 13804.6 0.0 140288.0 129170.6 102400.0 19730.9 54528.0 52078.9 7168.0 6684.3 10 0.143 2 0.172 0.315
13824.0 15872.0 13804.6 0.0 140288.0 129170.6 102400.0 19730.9 54528.0 52078.9 7168.0 6684.3 10 0.143 2 0.172 0.315
13824.0 15872.0 13804.6 0.0 140288.0 129170.6 102400.0 19730.9 54528.0 52078.9 7168.0 6684.3 10 0.143 2 0.172 0.315
13824.0 15872.0 13804.6 0.0 140288.0 129170.6 102400.0 19730.9 54528.0 52078.9 7168.0 6684.3 10 0.143 2 0.172 0.315

结果说明:

S0C 年轻代s0区容量
S1C 年轻代s1区容量
S0U 年轻代s0区使用量
S1U 年轻代s1区使用量
EC 年轻代eden区容量
EU 年轻代eden区容量
OC 老年代容量
OU 老年代使用量
MC 元空间容量
MU 元空间使用量
CCSC 压缩类空间容量
CCSU 压缩类空间使用大小
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc时间
FGC 从应用程序启动到采样时全gc次数
FGCT 从应用程序启动到采样时全gc时间
GCT 从应用程序启动到采样时gc用的总时间(s)

s0和s1中必然有一个的使用空间为0,用于复制交换
JDK 1.8以下MC/MU指标为PC/PU。(jdk1.8使用元空间替换了永久代)

通过参数的不同,可以进行某些方面的重点信息关注

选项作用
-class监视类装载、卸载数量、总空间以及类装载所耗费的时间
-gc监视Java堆状况,包括Eden区、两个survivor区、老年代、元空间的容量、已用空间、GC时间合计等信息
-gccapacity监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间
-gcutil监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
-gcnew监视新生代GC状况
-gcnewcapacity监视内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-gcold监视老年代状况
-gcoldcapacity监视内容与-gcold基本相同,输出主要关注用到的最大、最小空间
-gcmetacapacity输出元数据用到的最大、最小空间
-compiler输出JIT编译器编译过的方法、耗时等信息
-printcompilation输出已经被JIT编译的方法

jmap
可以用来查询堆中的实例或者进行堆dump等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//显示堆中对象统计信息,包括类、实例数、合计占用量

jmap -histo 6876 > a.txt


num #instances #bytes class name
----------------------------------------------
1: 652896 67501960 [B
2: 493614 47624832 [C
3: 54335 17164304 [I
4: 126862 3044688 java.lang.String
5: 28292 2489696 java.lang.reflect.Method
6: 27951 1786648 [Ljava.lang.Object;
7: 21992 1334608 [Lorg.springframework.boot.context.properties.source.ConfigurationPropertyName$ElementType;
8: 18122 1311816 [Ljava.util.HashMap$Node;
9: 32653 1306120 java.util.LinkedHashMap$Entry
10: 10985 1219992 java.lang.Class
11: 35321 1130272 java.util.concurrent.ConcurrentHashMap$Node
12: 17251 966056 java.util.LinkedHashMap
13: 29707 950624 java.util.HashMap$Node
14: 45305 908512 [Ljava.lang.Class;
15: 512 724368 [Ljava.util.concurrent.ConcurrentHashMap$Node;
16: 13340 640320 java.util.HashMap
17: 7763 604160 [S
18: 7641 550152 java.lang.reflect.Field
...

执行此命令需要将输出重定向到文件里,否则会有几千上万行

1
2
// 生成堆快照信息, 搬运者注:dump类操作,在生产环境慎用,STW警告
jmap -dump:format=b,file=heap.hprof 6876

jstack
用于生成java的线程快照信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jstack -l 6876 > threadDump.txt

执行结果:

2020-03-23 16:51:55
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.65-b01 mixed mode):

"DestroyJavaVM" #58 prio=5 os_prio=0 tid=0x0000000060c4a000 nid=0x6618 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:
- None

"http-nio-8601-Acceptor" #32 daemon prio=5 os_prio=0 tid=0x0000000060c49800 nid=0x824 runnable [0x00000000648de000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
- locked <0x00000000e0c3b670> (a java.lang.Object)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:463)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:73)
at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95)
at java.lang.Thread.run(Thread.java:745)

...

此命令也最好可以重定向到文件,输出结果较多

Java VisualVM工具介绍

VisualVM是目前为止JDK发布功能最强大的运行监控故障处理程序。

工具路径

在jdk路径的bin目录下,jvisualvm.exe

监控本地项目

双击工具,进入主界面,左侧即可看到本地的java进程,双击即可在右侧查看内容。

监控远程项目

1
2
3
// 执行java -jar启动命令时候加入以下参数,hostname为远程的主机名,port使用一个没被占用的端口

-Djava.rmi.server.hostname=xxxxxx -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.managementote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

整体情况监控

线程监控

对象情况监控

Eclipse Memory Analyzer工具安装使用

1、安装

eclipse –>help –> eclipse marketplace,输入memory,搜索出具体的插件,进行安装。

2、使用

使用jmap命令或者通过vusal工具dump下内存快照,拖进eclipse即可。

常用的JVM配置参数

参数名称含义默认值
-Xms初始堆大小物理内存的1/64
-Xmx最大堆大小物理内存的1/4
-XX:NewSize年轻代的初始大小
-XX:MaxNewSize年轻代的最大值
-Xmn年轻代的大小,如果通过-Xmn来配置新生代的内存大小,那么-XX:newSize = -XX:MaxNewSize = -Xmn
-XX:NewRatio年轻代与老年代的比例 4 表示 年轻代与老年代比例为 1:4
-XX:SurvivorRatio两个Survivor区与eden的比例 8 表示两个survivor:eden为 2:8

查看本机默认参数命令

1
java -XX:+PrintFlagsFinal > E:\ab.txt

如何查看高CPU占用的线程

1、找到java的进程

1
2
3
4
5
C:\Users\dell>jps -l

24352 cn.mastercom.MtexConfigApplication
30352
43508 sun.tools.jps.Jps

2、对具体进程进行线程dump

1
C:\Users\dell\Desktop>jstack -l 24352 > thread.txt

3、使用工具查看占用高的线程ID

linux通过top 命令进程查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
top -H -p 24352

top - 16:44:36 up 74 days, 1:22, 1 user, load average: 0.52, 0.39, 0.39
Threads: 30 total, 0 running, 30 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.0 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1882752 total, 92228 free, 980456 used, 810068 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 706556 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17063 root 20 0 2525764 138224 12788 S 0.0 7.3 0:00.00 java
17065 root 20 0 2525764 138224 12788 S 0.0 7.3 0:02.62 java
17067 root 20 0 2525764 138224 12788 S 0.0 7.3 0:05.73 java
17068 root 20 0 2525764 138224 12788 S 0.0 7.3 0:00.00 java
17069 root 20 0 2525764 138224 12788 S 0.0 7.3 0:00.00 java
17070 root 20 0 2525764 138224 12788 S 0.0 7.3 0:00.00 java
17071 root 20 0 2525764 138224 12788 S 0.0 7.3 0:07.06 java
17072 root 20 0 2525764 138224 12788 S 0.0 7.3 0:01.59 java
17073 root 20 0 2525764 138224 12788 S 0.0 7.3 0:00.00 java

可以看到CPU占用高的百分比

windwos可以使用ProcessExplorer软件进行查看

4、将线程ID转为16进制,因为线程dump的线程ID是使用16进制的。

1
2
3
43404 --> A98C

// 搬运者注:Linux下,通过`printf"%x\n" tid`命令转换从十六进制

5、去之前dump的线程映像中查看对应线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"Catalina-utility-1" #17 prio=1 os_prio=-2 tid=0x000000006130b000 nid=0xa98c waiting on condition [0x000000005e7be000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000e358e9a0> (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:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

Locked ownable synchronizers:
- None

如果是自己写的代码,可以从上面查看到具体的问题。

转载自: https://juejin.im/post/5e7eb285f265da79710a293d
作者:头疼梨
侵删