作为一名专业的java开发工程师,每每都会遇到JVM相关的问题,多在于full GC过多、超时,内存参数设置异常,或者查看异常的线程信息。
这些问题,本质上并不复杂,只要我们选择合适的工具就很简单。
那么有哪些工具呢?JDK自身就提供了很多可靠的工具。本文就来看下JDK提供的那些实用命令工具。
注意:笔者使用的是Windows机器,JDK安装位置为D:\Program Files\Java\jdk1.8.0_131
这些命令所在位置就是%JDK_HOME%\bin目录下
1.jinfo命令jinfo命令主要用于观察进程运行环境参数,包括java system属性和JVM命令行参数
1.1 命令参数jinfo命令主要使用方式如下:
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jinfo.exe
Usage:
jinfo [option]
(to connect to running process)
jinfo [option] jps
17648 Launcher
3424
1588 Jps
15276 Bootstrap
我们直接使用jinfo 15276 查看这个进程的相关信息
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jinfo.exe 15276
Attaching to process ID 15276, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
Java System Properties:
...
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
file.encoding = GBK
com.sun.management.jmxremote.password.file = C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest\jmxremote.password
java.specification.version = 1.8
...
java.vendor = Oracle Corporation
sun.java.launcher = SUN_STANDARD
catalina.base = C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
catalina.useNaming = true
os.name = Windows 10
VM Flags:
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=1073741824 -XX:+ManagementServer -XX:MaxHeapSize=1073741824 -XX:MaxNewSize=357564416 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=357564416 -XX:OldSize=716177408 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -Djava.util.logging.config.file=C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest\conf\logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xmx1G -Xms1G -Dcom.sun.management.jmxremote= -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest\jmxremote.password -Dcom.sun.management.jmxremote.access.file=C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest\jmxremote.access -Djava.rmi.server.hostname=127.0.0.1 -Djdk.tls.ephemeralDHKeySize=2048 -Djava.endorsed.dirs=D:\software\work-software\apache-tomcat-7.0.77\endorsed -Dcatalina.base=C:\Users\lucky\.IntelliJIdea2019.1\system\tomcat\Unnamed_SpringTest -Dcatalina.home=D:\software\work-software\apache-tomcat-7.0.77 -Djava.io.tmpdir=D:\software\work-software\apache-tomcat-7.0.77\temp
这样,所有的进程信息都打印出来了,直接查看即可。
2.jmap命令主要用于监视进程运行中的jvm物理内存的占用情况。
该进程内存中,所有对象的情况:产生了哪些对象,对象数量
2.1 命令参数PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jmap.exe
Usage:
jmap [option]
(to connect to running process)
jmap [option] .\jmap.exe -heap 15276
Attaching to process ID 15276, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 357564416 (341.0MB)
MaxNewSize = 357564416 (341.0MB)
OldSize = 716177408 (683.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 = 175112192 (167.0MB)
used = 175064104 (166.95413970947266MB)
free = 48088 (0.04586029052734375MB)
99.97253874818722% used
From Space:
capacity = 82313216 (78.5MB)
used = 0 (0.0MB)
free = 82313216 (78.5MB)
0.0% used
To Space:
capacity = 91226112 (87.0MB)
used = 0 (0.0MB)
free = 91226112 (87.0MB)
0.0% used
PS Old Generation
capacity = 716177408 (683.0MB)
used = 712284288 (679.2872314453125MB)
free = 3893120 (3.7127685546875MB)
99.45640284704429% used
16144 interned Strings occupying 2101048 bytes.
特别详细的展示了堆内存空间的各种参数和当前使用情况
2.3 jmap -histo [pid]列出当前内存中存活对象的柱状图,也就是对象个数以及占用内存信息
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jmap.exe -histo 15276
num #instances #bytes class name
----------------------------------------------
1: 118 864029648 [Ljava.lang.Byte;
2: 48058 9365704 [C
3: 1435 2274008 [B
4: 3179 1411832 [I
5: 45365 1088760 java.lang.String
6: 5795 660416 java.lang.Class
7: 12701 406432 java.util.HashMap$Node
8: 4459 392392 java.lang.reflect.Method
9: 6174 373728 [Ljava.lang.Object;
10: 10104 323328 java.util.concurrent.ConcurrentHashMap$Node
11: 1184 209456 [Ljava.util.HashMap$Node;
12: 2292 128912 [Ljava.lang.String;
13: 160 127328 [Ljava.util.concurrent.ConcurrentHashMap$Node;
...
平时参考意义不是很大,但是如果发生内存泄露等问题时,还有有参考价值的。
2.4 jmap -clstats [pid]主要展示ClassLoader信息
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jmap.exe -clstats 15276
Attaching to process ID 15276, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.131-b11
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness.....................................................liveness analysis may be inaccurate ...
class_loader classes bytes parent_loader alive? type
2810 4889269 null live
0x00000000c0057f00 99 282157 0x00000000c0057f60 live sun/misc/Launcher$AppClassLoader@0x000000010000f6a0
0x00000000c06554a0 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000c0655ae0 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000c0656120 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000d75fd4d8 1 880 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000f4cf7908 1 1471 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000c0655568 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000c0655ba8 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000d75fccd0 1 878 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000d75fd410 1 880 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
0x00000000c0655630 1 1473 0x00000000c0118260 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8
...
2.5 jmap -dump:format=b,file=heapdump.phrof [pid]
这个比较有用,可以打印出当前进程的dump信息
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jmap.exe -dump:format=b,file=heapdump.phrof 15276
Dumping heap to D:\Program Files\Java\jdk1.8.0_131\bin\heapdump.phrof ...
Heap dump file created
这里打印出来的headdump.phrof文件,可以通过专业工具进行分析。后续笔者再在其他文章中进行单独分析。
3.jstatjstat命令利用JVM内建的指令对java应用程序的资源和性能进行实时的命令行的监控,包括对进程的ClassLoader、compile、gc等情况。
3.1 命令参数PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstat.exe
invalid argument count
Usage: jstat -help|-options
jstat - [-t] [-h] [ []]
Definitions:
An option reported by the -options option
Virtual Machine Identifier. A vmid takes the following form:
[@[:]]
Where is the local vm identifier for the target
Java virtual machine, typically a process id; is
the name of the host running the target Java virtual machine;
and is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
Number of samples between header lines.
Sampling interval. The following forms are allowed:
["ms"|"s"]
Where is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
Number of samples to take before terminating.
-J Pass directly to the runtime system.
我们看这个命令的组成,中间具体的 有哪些呢?可以通过jstat -options来获取
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstat.exe -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation
貌似也没个解释,笔者从网上找来各个option的含义,如下所示:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不同的generations(包括新生区,老年区,permanent区)相应的heap容量情况
-gccause:统计gc的情况,(同-gcutil)和引起gc的事件
-gcnew:统计gc时,新生代的情况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的情况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap情况
看样子统计了很多heap gc情况。
有那么多,具体使用的时候用哪个呢?当然你如果记得那就找对应的option即可,如果像笔者一样懒,那就记得两个option即可:-gc -gcutil
3.2 jstat -gc [pid] [ []]-gc命令查看当前进程的GC情况,包括老年代和新生代
这里的interval默认单位是ms,count是总打印次数
# 每秒打印一条当前jvm gc信息,总共打印10条
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstat.exe -gc 15276 1000 10
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
80384.0 89088.0 0.0 0.0 171008.0 169991.8 699392.0 695588.3 34176.0 33212.7 3968.0 3650.6 10 0.635 121 29.864 30.499
80384.0 89088.0 0.0 0.0 171008.0 170271.4 699392.0 695588.3 34176.0 33212.7 3968.0 3650.6 10 0.635 123 30.134 30.769
80384.0 89088.0 0.0 0.0 171008.0 166144.3 699392.0 695589.5 34176.0 33220.1 3968.0 3650.6 10 0.635 127 31.064 31.699
80384.0 89088.0 0.0 0.0 171008.0 171008.0 699392.0 695589.4 34176.0 33221.4 3968.0 3650.6 10 0.635 131 32.047 32.682
80384.0 89088.0 0.0 0.0 171008.0 171008.0 699392.0 695589.4 34176.0 33223.4 3968.0 3650.6 10 0.635 135 33.045 33.680
80384.0 89088.0 0.0 0.0 171008.0 170952.0 699392.0 695589.4 34176.0 33227.1 3968.0 3651.1 10 0.635 139 34.043 34.679
80384.0 89088.0 0.0 0.0 171008.0 171008.0 699392.0 695589.4 34176.0 33227.2 3968.0 3651.1 10 0.635 144 35.107 35.742
80384.0 89088.0 0.0 0.0 171008.0 171008.0 699392.0 695589.4 34176.0 33231.3 3968.0 3651.2 10 0.635 148 36.068 36.703
80384.0 89088.0 0.0 0.0 171008.0 166260.6 699392.0 695587.7 34176.0 33243.8 3968.0 3651.7 10 0.635 152 37.235 37.870
80384.0 89088.0 0.0 0.0 171008.0 167297.5 699392.0 695587.7 34176.0 33245.0 3968.0 3651.7 10 0.635 156 38.156 38.792
这些输出值都是什么含义呢?
S0C:Current survivor space 0 capacity (KB).
S1C:Current survivor space 1 capacity (KB).
S0U:Current survivor space 0 utilization (KB).
S1U:Current survivor space 1 utilization (KB).
EC:Current eden space capacity (KB).
EU:Eden space utilization (KB).
OC:Current old space capacity (KB).
OU:Old space utilization (KB).
PC:Current permanent space capacity (KB).
PU:Permanent space utilization (KB).
YGC:Number of young generation GC Events.
YGCT:Young generation garbage collection time.
FGC:Number of full GC events.
FGCT:Full garbage collection time.
GCT:Total garbage collection time.
重点需要关注哪些呢?
笔者觉得比较重要的是YGC(young gc发生的次数)、YGCT(young gc所消耗的时间)、FGC(full gc发生的次数)、FGCT(full gc所消耗的时间)
只关注这些就可以了。
3.3 jstat -gcutil [pid] [ []]这个与-gc有什么区别呢?
本质上来说,基本没有区别,唯一的区别就是,-gcutil按照百分比的方式来展示的
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstat.exe -gcutil 15276 1000 10
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 98.80 99.46 96.85 92.08 10 0.635 304 78.144 78.779
0.00 0.00 98.80 99.46 96.85 92.08 10 0.635 304 78.144 78.779
0.00 0.00 98.80 99.46 96.85 92.08 10 0.635 304 78.144 78.779
0.00 0.00 98.82 99.46 96.85 92.08 10 0.635 305 78.144 78.779
0.00 0.00 97.68 99.45 96.92 92.08 10 0.635 309 79.405 80.040
0.00 0.00 97.64 99.45 96.94 92.08 10 0.635 314 80.540 81.175
0.00 0.00 97.64 99.45 96.94 92.08 10 0.635 318 81.559 82.195
0.00 0.00 97.64 99.45 96.94 92.08 10 0.635 321 82.466 83.101
0.00 0.00 97.64 99.45 96.94 92.08 10 0.635 324 83.356 83.991
0.00 0.00 97.64 99.45 96.94 92.08 10 0.635 328 84.654 85.289
可以看到,前面展示的Eden区 s1 s0区的展示变成百分比的方式了
4.jstackjstack这个命令平时我们应该用的比较多。主要用来查看当前进程的线程信息
4.1 命令参数PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstack.exe
Usage:
jstack [-l]
(to connect to running process)
jstack -F [-m] [-l]
(to connect to a hung process)
jstack [-m] [-l]
(to connect to a core file)
jstack [-m] [-l] [server_id@]
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
4.2 jstack [pid]
直接查看进程线程信息。
这样直接使用的话,会把进程的线程信息直接打印到控制台,一般来说,都打印到一个文件里
PS D:\Program Files\Java\jdk1.8.0_131\bin> .\jstack.exe 15276 > stack.txt
直接打印到当前目录下 stack.txt文件里。我们看下该文件内容:
2022-03-20 18:47:07
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):
"http-bio-8080-exec-76" #117 daemon prio=5 os_prio=0 tid=0x0000000017c37800 nid=0x5070 waiting on condition [0x000000001fb0e000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for (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:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
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:748)
...
当前线程名(http-bio-8080-exec-76)、线程状态(WAITING)、等待条件(0x000000001fb0e000)等信息都打印出来了。
总结:JDK的工具中当然不止笔者上述介绍的这些,但是最常用的也就是目前这些了。
熟练掌握工具,不再惧怕任何问题。