1. 简介
Oracle JDK在发布的SDK包中,除了核心类库、编译、运行和调试工具,还带了大量的监视查看工具。这些监视工具可以辅助开发者查看Java程序的运行状态,包括环境配置、内存、CPU、线程、堆栈、类加载等各种信息。本文将对这些监视工具进行介绍。
本文的工具介绍将主要参考JDK 8的官方文档,并以jdk 8为运行环境演示命令输出。
2. 官方文档
2. 各个工具简介
2.1 基本工具
- java 启动java应用程序命令
- javac 编译工具,将java源代码编译为java bytecode文件
- jar 打包归档工具,也可以对包进行解压
- javadoc 生成java api说明和使用文档工具
基本工具的使用方法可参考这篇文章。
2.1 常用的监控分析工具
- jps 查看java进程状态工具
- jstat 内存监视工具
- jmap 内存查看工具,获取内存快照
- jinfo 获取java进程的环境配置信息
- jstack 进程中各个线程的调用栈查看工具
- jconsole 一个java进程的监视和管理控制台,可以查看进程的CPU、内存、线程运行情况,还可以辅助检测死锁,查看类加载详细情况
-
jvisualvm.exe 一个可视化的java进程管理工具,可以查看进程的CPU、内存、线程运行情况。
上述监控分析工具的使用方法见下文。
3. 工具使用方法
3.1 jps
- 简单的命令,输出进程id:jps
- 输出进程的id,main程序全package路径名,启动参数:jps -mlvV
- 还可以配合jstat server/RMI registry,实现查看远程服务器上的java进程列表:jps -l remote.domain
一个命令输出样例如下,
$ jps -mlvV
16552 jdk.jcmd/sun.tools.jps.Jps -mlvV -Dapplication.home=G:\local\java\jdk-9.0.1 -Xms8m -Djdk.module.main=jdk.jcmd
3.2 jstat
检查进程15644的内存使用(garbage collected heap)情况
$ jstat -gc 17152
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
10752.0 10752.0 0.0 0.0 64512.0 2580.5 172032.0 0.0 4480.0 770.2 384.0 75.9 0 0.000 0 0.000 0.000
每隔5秒检查进程15644的内存使用情况,输出中添加timestamp,每隔5行添加header,
$ jstat -gcutil -t -h5 15644 500
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
2926.3 0.00 89.58 32.70 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2926.8 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2927.3 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2927.8 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2928.3 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
2928.8 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2929.3 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2929.8 0.00 89.58 33.13 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2930.3 0.00 89.58 33.93 1.42 94.99 90.00 7 0.025 1 0.012 0.037
2930.8 0.00 89.58 34.16 1.42 94.99 90.00 7 0.025 1 0.012 0.037
在使用-gc选项来检查内存使用情况时,各个缩写字母的含义为,
- S0C: Current survivor space 0 capacity (KB). 年轻代第一个survivor空间容量
- S1C: Current survivor space 1 capacity (KB). 年轻代第二个survivor空间容量
- S0U: Survivor space 0 utilization (KB). 年轻代第一个survivor空间使用量
- S1U: Survivor space 1 utilization (KB). 年轻代第二个survivor空间使用量
- EC: Current eden space capacity (KB). 年轻代Eden空间容量
- EU: Eden space utilization (KB). 年轻代Eden空间使用量
- OC: Current old space capacity (KB). 年老代空间容量
- OU: Old space utilization (KB). 年老代空间使用量
- MC: Metaspace capacity (KB). 元空间容量
- MU: Metaspace utilization (KB). 元空间使用量
- CCSC: Compressed class space capacity (KB).
- CCSU: Compressed class space used (KB).
- YGC: Number of young generation garbage collection (GC) events. 年轻代空间GC次数
- YGCT: Young generation garbage collection time. 年轻代空间GC时间开销
- FGC: Number of full GC events. 整个堆区FULL GC次数
- FGCT: Full garbage collection time. 整个堆区FULL GC时间开销
- GCT: Total garbage collection time. 总GC时间开销
其它内存检查时所看到的缩写字母类似,更详细可以查看官方文档。
JVM的内存空间模型详细介绍见这里。
3.3 jmap
简单命令,输出进程14120的内存使用情况,
$ jmap -heap 14120
Attaching to process ID 14120, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4208984064 (4014.0MB)
NewSize = 88080384 (84.0MB)
MaxNewSize = 1402994688 (1338.0MB)
OldSize = 176160768 (168.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 31981568 (30.5MB)
used = 21396720 (20.405502319335938MB)
free = 10584848 (10.094497680664062MB)
66.90328629290471% used
From Space:
capacity = 1048576 (1.0MB)
used = 524288 (0.5MB)
free = 524288 (0.5MB)
50.0% used
To Space:
capacity = 1048576 (1.0MB)
used = 0 (0.0MB)
free = 1048576 (1.0MB)
0.0% used
PS Old Generation
capacity = 176160768 (168.0MB)
used = 68010784 (64.86013793945312MB)
free = 108149984 (103.13986206054688MB)
38.60722496396019% used
6179 interned Strings occupying 524264 bytes.
导出进程14120的内存使用情况到文件dump.tmp中,
jmap -dump:format=b,file=dump.tmp 14120
然后就可以使用 eclipse memory analyzer 进行分析,查看是否有内存泄露。
3.4 jinfo
获取进程14120的环境配置信息
$ jinfo 14120
Attaching to process ID 14120, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14
Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.111-b14
sun.boot.library.path = C:\Program Files\Java\jdk1.8.0_111\jre\bin
...
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
java.runtime.version = 1.8.0_111-b14
java.library.path = C:\Program Files\Java\jdk1.8.0_111\bin;
java.class.version = 52.0
...
os.name = Windows 7
sun.cpu.isalist = amd64
VM Flags:
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=264241152 -XX:MaxHeapSize=4208984064 -XX:MaxNewSize=1402994688 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=88080384 -XX:OldSize=176160768 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line:
3.5 jstack
获取进程14120的线程调用堆栈信息,通过查看线程堆栈,可以了解是否有死锁情况在发生(可以使用jconsole来辅助查看)。
$ jstack -l 14120
2018-08-22 17:49:07
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):
"RMI TCP Connection(35)-172.20.16.89" #47 daemon prio=5 os_prio=0 tid=0x000000001d685000 nid=0x50f0 runnable [0x0000000021aee000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000006c9442578> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:550)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1/1179263329.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- <0x000000076cd9db08> (a java.util.concurrent.ThreadPoolExecutor$Worker)
...
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001bfa9000 nid=0x468c in Object.wait() [0x000000001d3be000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000006c54ddeb0> (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)
...
"main" #1 prio=5 os_prio=0 tid=0x000000000253f000 nid=0x45d0 waiting on condition [0x000000000295f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.pphh.demo.Application.main(Application.java:23)
Locked ownable synchronizers:
- None
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000000000238c800 nid=0x4404 runnable
...
JNI global references: 252