系统稳定性—JVM监控和调优常用命令总结
一、JVM监控和调优
在Java
应用和服务出现莫名的卡顿、CPU
飙升等问题时总是要分析一下对应进程的JVM
状态以定位问题和解决问题并作出相应的优化,在这过程中Java
自带的一些状态监控命令和图形化工具就非常方便了。本文总结了最常用的命令行工具及其常用参数解释,图形化监控工具的用法,仅供参考。
命令 | 说明 | 常用形式 |
---|---|---|
jps | 查看java进程pid | jps |
jinfo | 查看jvm环境与参数 | jinfo pid > a.txt |
jstat | 查看jvm内存与GC状态 | jstat -gcutil pid 1000 10 |
jstack | 查看jvm当前时刻的线程快照 | jstack -l pid |
jmap | 生成dump文件 | jmap -dump:live,format=b,file=dump.hprof pid |
jmap | 生成统计信息 | jmap -histo pid |
jhat | dump分析工具 | jhat dump.hprof |
下面主要围绕这几个命令分别进行说明
二、命令
2.1 jps
Java
版的ps
命令,查看java
进程及其相关的信息,如果你想找到一个java
进程的pid
,那可以用jps
命令替代linux
中的ps
命令了,简单而方便。
2.1.1 命令格式
jps [options] [hostid]
options
参数解释:
- -l:输出主类全名或
jar
路径 - -q:只输出
LVMID
- -m:输出
JVM
启动时传递给main()
的参数 - -v:输出
JVM
启动时显示指定的JVM
参数
2.1.2 常用示例
jps -l 输出jar包路径,类全名
jps -m 输出main参数
jps -v 输出JVM参数
2.2 jinfo
jinfo
是用来查看JVM
参数和动态修改部分JVM
参数的命令
2.2.1 命令格式
jinfo [option]
options
参数解释:
- -flag
打印指定名称的参数 - -flag [+|-]
打开或关闭参数 - -flag
= 设置参数 - -flags 打印所有参数
- -sysprops 打印系统配置
打印上面两个选项
2.2.2 常用示例
其中11666
为pid
jinfo 11666
查看JVM
参数和系统配置
jinfo -flags 11666
jinfo -sysprops 11666
查看打印GC日志参数
jinfo -flag PrintGC 11666
jinfo -flag PrintGCDetails 11666
打开GC
日志参数
jinfo -flag +PrintGC 11666
jinfo -flag +PrintGCDetails 11666
关闭GC日志参数
jinfo -flag -PrintGC 11666
jinfo -flag -PrintGCDetails 11666
还可以使用下面的命令查看那些参数可以使用jinfo
命令来管理:
java -XX:+PrintFlagsFinal -version | grep manageable
常用JVM
参数:
-Xms
:初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio
参数可以调整)空余堆内存小于40%
时,JVM
就会增大堆直到-Xmx
的最大限制
-Xmx
:最大堆大小,默认(MaxHeapFreeRatio
参数可以调整)空余堆内存大于70%
时,JVM
会减少堆直到-Xms
的最小限制
-Xmn
:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space
)。与jmap -heap
中显示的New gen
是不同的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小
。在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,
Sun
官方推荐配置为整个堆的3/8
。
-XX:SurvivorRatio
:新生代中Eden
区域与Survivor
区域的容量比值,默认值为8
。两个Survivor
区与一个Eden
区的比值为2:8
,一个Survivor
区占整个年轻代的1/10
。
-Xss
:每个线程的堆栈大小。JDK5.0
以后每个线程堆栈大小为1M
,以前每个线程堆栈大小为256K
。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000
左右。一般小的应用,如果栈不是很深,应该是128k
够用的,大的应用建议使用256k
。这个选项对性能影响比较大,需要严格的测试。和threadstacksize
选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"-Xss is translated in a VM flag named ThreadStackSize”一般设置这个值就可以了。
-XX:PermSize
:设置永久代(perm gen
)初始值。默认值为物理内存的1/64
。
-XX:MaxPermSize
:设置持久代最大值。物理内存的1/4
。
2.3 jstat
jstat
命令是使用频率比较高的命令,主要用来查看JVM
运行时的状态信息,包括内存状态、垃圾回收等。
2.3.1 命令格式
jstat [option] LVMID [interval] [count]
其中LVMID
是进程id
,interval
是打印间隔时间(毫秒),count
是打印次数(默认一直打印)
option参数解释:
- -class class loader的行为统计
- -compiler HotSpt JIT编译器行为统计
- -gc 垃圾回收堆的行为统计
- -gccapacity 各个垃圾回收代容量(young,old,perm)和他们相应的空间统计
- -gcutil 垃圾回收统计概述
- -gccause 垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因
- -gcnew 新生代行为统计
- -gcnewcapacity 新生代与其相应的内存空间的统计
- -gcold 年老代和永生代行为统计
- -gcoldcapacity 年老代行为统计
- -gcpermcapacity 永生代行为统计
- -printcompilation HotSpot编译方法统计
2.3.2 常用示例
root@8d36124607a0:/# jstat -gcutil 11666 1000 3
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
6.17 0.00 6.39 33.72 93.42 90.57 976 57.014 68 53.153 110.168
6.17 0.00 6.39 33.72 93.42 90.57 976 57.014 68 53.153 110.168
6.17 0.00 6.39 33.72 93.42 90.57 976 57.014 68 53.153 110.168
jstat -gcutil 11666 1000 3
11666
为pid
,每隔1000
毫秒打印一次,打印3
次
字段解释:
- S0 survivor0使用百分比
- S1 survivor1使用百分比
- E Eden区使用百分比
- O 老年代使用百分比
- M 元数据区使用百分比
- CCS 压缩使用百分比
- YGC 年轻代垃圾回收次数
- YGCT 年轻代垃圾回收消耗时间
- FGC 老年代垃圾回收次数
- FGCT 老年代垃圾回收消耗时间
- GCT 垃圾回收消耗总时间
-gc
和-gcutil
参数类似,只不过输出字段不是百分比,而是实际的值。
root@8d36124607a0:/# jstat -gc 11666 1000 3
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
25600.0 25600.0 0.0 1450.0 204800.0 97460.7 512000.0 172668.8 345736.0 322997.7 48812.0 44209.0 977 57.040 68 53.153 110.193
25600.0 25600.0 0.0 1450.0 204800.0 97460.7 512000.0 172668.8 345736.0 322997.7 48812.0 44209.0 977 57.040 68 53.153 110.193
25600.0 25600.0 0.0 1450.0 204800.0 97460.7 512000.0 172668.8 345736.0 322997.7 48812.0 44209.0 977 57.040 68 53.153 110.193
字段解释:
- S0C survivor0大小
- S1C survivor1大小
- S0U survivor0已使用大小
- S1U survivor1已使用大小
- EC Eden区大小
- EU Eden区已使用大小
- OC 老年代大小
- OU 老年代已使用大小
- MC 方法区大小
- MU 方法区已使用大小
- CCSC 压缩类空间大小
- CCSU 压缩类空间已使用大小
- YGC 年轻代垃圾回收次数
- YGCT 年轻代垃圾回收消耗时间
- FGC 老年代垃圾回收次数
- FGCT 老年代垃圾回收消耗时间
- GCT 垃圾回收消耗总时间
2.4 jstack
jstack
是用来查看JVM
线程快照的命令,线程快照是当前JVM
线程正在执行的方法堆栈集合。使用jstack
命令可以定位线程出现长时间卡顿的原因,例如死锁,死循环等。jstack
还可以查看程序崩溃时生成的core
文件中的stack
信息。
2.4.1 命令格式
jstack [-l] <pid> (连接运行中的进程)
jstack -F [-m] [-l] <pid> (连接挂起的进程)
jstack [-m] [-l] <executable> <core> (连接core文件)
jstack [-m] [-l] [server_id@]<remote server IP or hostname> (连接远程debug服务器)
option参数解释:
- -F 当使用jstack
无响应时,强制输出线程堆栈。 - -m 同时输出java和本地堆栈(混合模式)
- -l 额外显示锁信息
2.4.2 常用示例
jstack -l 11666 | more
输出信息:
2024-06-08 10:14:45
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):
"Attach Listener" #10 daemon prio=9 os_prio=31 tid=0x00007fb54485a000 nid=0x3503 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fb5430b4000 nid=0x3203 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C1 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fb54407e800 nid=0x3103 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fb54400f800 nid=0x4203 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fb54285a000 nid=0x4403 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fb5430ab000 nid=0x4503 runnable [0x0000700002427000]
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:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000079570b9e0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000079570b9e0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47)
Locked ownable synchronizers:
- None
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fb544026000 nid=0x4603 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fb544817000 nid=0x5103 in Object.wait() [0x0000700002098000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795588ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x0000000795588ed0> (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)
Locked ownable synchronizers:
- None
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fb54303f800 nid=0x2c03 in Object.wait() [0x0000700001f95000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0000000795586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"main" #1 prio=5 os_prio=31 tid=0x00007fb54280e000 nid=0xe03 waiting on condition [0x0000700001983000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.jvm.JVMtest.main(JVMtest.java:6)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=31 tid=0x00007fb544816800 nid=0x5303 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fb544009800 nid=0x2507 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fb54300f800 nid=0x2403 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fb543010000 nid=0x2303 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fb543010800 nid=0x2a03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fb5430b4800 nid=0x3f03 waiting on condition
JNI global references: 15
你会发现上面的信息其实是一段一段的,摘取其中的一段为大家说明:
"main" #1 prio=5 os_prio=31 tid=0x00007fb54280e000 nid=0xe03 waiting on condition [0x0000700001983000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.jvm.JVMtest.main(JVMtest.java:6)
字段解释:
- main: 线程名称
#1
: 当前线程ID,从main开始,jvm会根据线程创建的顺序为其线程编号- prio: 优先级的顺序,一般默认是5
- os_prio: 线程对应系统的优先级
- tid: java内的线程id
- nid: 操作系统级别的线程id,是一个十六进制
关于线程的信息:
- NEW: 线程新建,还没开始运行
- RUNNABLE: 正在java虚拟机中运行的线程
- BLOCKED: 被阻塞,正在等待监视器锁的线程
- WAITING: 无限期等待另一个线程执行特定操作的线程
- TIMED_WAITING: 等待另一个线程执行操作达到指定等待时间的线程
- TERMINATED: 已经退出的线程
我们这里关注的最多的就是nid
2.5 jmap
jmap
是用来生成堆dump
文件和查看堆相关的各类信息的命令,例如查看finalize
执行队列,heap
的详细信息和使用情况。
2.5.1 命令格式
jmap [option]
jmap [option] <executable
jmap [option] [server_id@]
option参数解释:
-
to print same info as Solaris pmap -
-heap 打印java heap摘要
-
-histo[:live] 打印堆中的java对象统计信息
-
-clstats 打印类加载器统计信息
-
-finalizerinfo 打印在f-queue中等待执行finalizer方法的对象
-
-dump:
生成java堆的dump文件
dump-options:
live:只转储存活的对象,如果没有指定则转储所有对象
format=b:二进制格式
file=:转储文件到 -
-F 强制选项
2.5.2 常用示例
jmap -dump:live,format=b,file=dump.hprof 11666
输出:
Dumping heap to /dump.hprof ... Heap dump file created
这个命令是要把java
堆中的存活对象信息转储到dump.hprof
文件
jmap -finalizerinfo 11666
输出:
Attaching to process ID 11666, please wait... Debugger attached successfully.Server compiler detected.JVM version is 24.71-b01 Number of objects pending for finalization: 0
输出结果的含义为当前没有在等待执行finalizer
方法的对象
jmap -heap 11666
输出堆的详细信息
输出:
Attaching to process ID 11666, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.25-b02 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: //堆内存初始化配置 MinHeapFreeRatio = 0 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40) MaxHeapFreeRatio = 100 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70) MaxHeapSize = 1073741824 (1024.0MB) //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小 NewSize = 22020096 (21.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的新生代的默认大小 MaxNewSize = 357564416 (341.0MB) //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的新生代的最大大小 OldSize = 45088768 (43.0MB) //对应jvm启动参数-XX:OldSize=<value>:设置JVM堆的老年代的大小 NewRatio = 2 //对应jvm启动参数-XX:NewRatio=:新生代和老生代的大小比率 SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置新生代中Eden区与Survivor区的大小比值 MetaspaceSize = 21807104 (20.796875MB) // 元数据区大小 CompressedClassSpaceSize = 1073741824 (1024.0MB) //类压缩空间大小 MaxMetaspaceSize = 17592186044415 MB //元数据区最大大小 G1HeapRegionSize = 0 (0.0MB) //G1垃圾收集器每个Region大小 Heap Usage: //堆内存使用情况 PS Young Generation Eden Space: //Eden区内存分布 capacity = 17825792 (17.0MB) //Eden区总容量 used = 12704088 (12.115562438964844MB) //Eden区已使用 free = 5121704 (4.884437561035156MB) //Eden区剩余容量 71.26801434685203% used //Eden区使用比率 From Space: //其中一个Survivor区的内存分布 capacity = 2097152 (2.0MB) used = 1703936 (1.625MB) free = 393216 (0.375MB) 81.25% used To Space: //另一个Survivor区的内存分布 capacity = 2097152 (2.0MB) used = 0 (0.0MB) free = 2097152 (2.0MB) 0.0% used PS Old Generation capacity = 52428800 (50.0MB) //老年代容量 used = 28325712 (27.013504028320312MB) //老年代已使用 free = 24103088 (22.986495971679688MB) //老年代空闲 54.027008056640625% used //老年代使用比率 15884 interned Strings occupying 2075304 bytes.
jmap -histo:live 11666 | more
输出存活对象统计信息
输出:
num #instances #bytes class name ---------------------------------------------- 1: 46608 1111232 java.lang.String 2: 6919 734516 java.lang.Class 3: 4787 536164 java.net.SocksSocketImpl 4: 15935 497100 java.util.concurrent.ConcurrentHashMap$Node 5: 28561 436016 java.lang.Object
2.6 jhat
jhat
是用来分析jmap
生成dump
文件的命令,jhat
内置了应用服务器,可以通过网页查看dump
文件分析结果,jhat
一般是用在离线分析上。
2.6.1 命令格式
jhat [option] [dumpfile]
option参数解释:
- -stack false: 关闭对象分配调用堆栈的跟踪
- -refs false: 关闭对象引用的跟踪
- -port
: HTTP服务器端口,默认是7000 - -debug
: debug级别
0: 无debug输出
1: Debug hprof file parsing
2: Debug hprof file parsing, no server
- -version分析报告版本
2.6.2 常用示例
jhat dump.hprof
2.7 jconsole,jvisualvm
除了以上所述的常用命令行工具之外还有图形化工具,使用上比较简单。在命令行下执行jconsole
和jvisualvm
即可。具体的用法,自己去点一点就了解了。