本章就JVM罕用的运维与调试工具做一些简略的梳理。
一、JDK自带工具
本地环境装置过残缺JDK包之后,会在$JAVA_HOME/bin
目录下发现很多工具,包含编译命令javac
、执行命令java
等。本节咱们梳理一下其中用于JVM运维的工具。
本节形容的JDK自带工具以OpenJDK11为准。
1.1 jps
jps是JVM过程查找工具,相似于linux的ps
命令。咱们应用这个命令次要是为了找到以后正在运行的JVM及其过程ID。
jps应用示例:
zhaochun@zhaochun-T480:bin$ jps
30816 Main
5842 Jps
484 Launcher
22137 TestCase01GCTest
32573 EchoBackService
jps反对的参数选项:
选项 | 作用 |
---|---|
-q | 只输入JVM过程ID |
-m | 输入启动JVM时,传给启动类main函数的参数 |
-l | 输入启动类完整包名,如果是jar包,则输入jar门路 |
-v | 输入JVM启动时传入的JVM参数 |
示例,启动如下这样一个JVM
# JVM参数:-Xms20M -Xmx20M -Xmn10M -XX:+UseG1GC -XX:MaxGCPauseMillis=200
# main函数:czhao.study.jvm.TestCase01GCTest
# main函数参数:testParam
/usr/java/jdk-11.0.7+10/bin/java -Xms20M -Xmx20M -Xmn10M -XX:+UseG1GC -XX:MaxGCPauseMillis=200 czhao.study.jvm.TestCase01GCTest testParam
# over
各种选项输入如下:
# 只输入JVM过程ID
zhaochun@zhaochun-T480:bin$ jps -q
30816
484
10517
11116
32573
# 输入main函数参数
zhaochun@zhaochun-T480:bin$ jps -m | grep TestCase01GCTest
10517 TestCase01GCTest testParam
# 输入启动类完整包名
zhaochun@zhaochun-T480:bin$ jps -l | grep TestCase01GCTest
10517 czhao.study.jvm.TestCase01GCTest
# 输入JVM启动时传入参数
zhaochun@zhaochun-T480:bin$ jps -v | grep TestCase01GCTest
10517 TestCase01GCTest -Xms20M -Xmx20M -Xmn10M -XX:+UseG1GC -XX:MaxGCPauseMillis=200
1.2 jstat
jstat是用于监督JVM各种运行时的状态信息的命令行工具,包含类加载、内存、垃圾收集、即时编译等运行时数据。
jstat应用示例:
# 先应用jps查找指标JVM的过程ID
zhaochun@zhaochun-T480:bin$ jps | grep TestCase01GCTest
10517 TestCase01GCTest
# 应用jstat查看指标过程 10517 的垃圾收集情况,每500ms查看一次,共查看 10 次
# 如果不加前面两个参数,则示意只查看一次
# -gc 示意查看的是垃圾收集情况
zhaochun@zhaochun-T480:bin$ jstat -gc 10517 500 10
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
0.0 1024.0 0.0 1024.0 5120.0 0.0 14336.0 13816.0 4864.0 3446.8 512.0 305.8 1 0.001 0 0.000 0.001
上述-gc
示意查看垃圾收集情况,其中各列的含意如下(内存空间单位:KB,工夫单位:秒):
- S0C :幸存者0区容量
- S1C :幸存者1区容量
- S0U :幸存者0区已应用大小
- S1U :幸存者1区已应用大小
- EC :eden区容量
- EU :eden区已应用大小
- OC :老年代容量
- OU :老年代已应用大小
- MC :元数据空间容量
- MU :元数据空间已应用大小
- CCSC :压缩类空间容量
- CCSU :压缩类空间已应用大小
- YGC :年老代GC次数
- YGCT :年老代GC耗时共计
- FGC :整堆GC次数
- FGCT :整堆GC耗时共计
- GCT :所有GC耗时共计
其中,CCS
是压缩类空间,用于对象指针与类指针压缩,属于MetaSpace元数据空间的一部分,通过-XX:+UseCompressedClassPointers
和-XX:+UseCompressedOops
开启,默认开启。
-gc
是参数选项,能够替换为以下其余选项:
选项 | 作用 |
---|---|
-gc | 监督JVM的堆情况,包含eden区、两个幸存者区、老年代的容量、已用空间、以及GC工夫共计等等 |
-gccapacity | 与-gc 基本相同,但关注点在于JVM堆中各个区的历史最大、最小应用空间 |
-gcutil | 与-gc 基本相同,但关注点在于已应用空间占总空间的百分比 |
-gccause | 与-gcutil 基本相同,但会额定输入最近一次GC起因 |
-gcnew | 监督新生代GC情况 |
-gcnewcapacity | 与-gcnew 基本相同,但关注点在于历史最大最小已应用空间 |
-gcold | 监督老年代GC情况 |
-gcoldcapacity | 与-gcold 基本相同,但关注点在于历史最大最小已应用空间 |
-gcmetacapacity | 监督元数据空间,重点关注历史最大最小已应用空间 |
-class | 监督类加载、卸载数量、总空间及类装载消耗工夫 |
-compiler | 输入即时编译器编译过的办法、耗时等 |
-printcompilation | 输入曾经被即时编译的办法 |
1.3 jinfo
jinfo用于查看JVM参数信息,并能够实时调整大量能够在运行时扭转的参数。
应用jinfo时要留神两点:
- 执行
jinfo
命令的用户与指标JVM的启动用户应该是同一个用户,防止权限有余。 jinfo
命令版本与指标JVM的java
命令版本统一,应该是同一个JDK目录下的命令。
查看指标JVM的残缺JVM参数信息如下所示:
# 因为之前启动的JVM应用的是 /usr/java/jdk-11.0.7+10/bin/java ,所以这里须要应用对应雷同目录下的 jinfo
zhaochun@zhaochun-T480:bin$ pwd
/usr/java/jdk-11.0.7+10/bin
zhaochun@zhaochun-T480:bin$ ./jinfo 10517
Java System Properties:
#Mon Feb 08 11:49:06 CST 2021
sun.desktop=gnome
awt.toolkit=sun.awt.X11.XToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=UTF-8
java.class.path=.\:/usr/java/jdk1.8.0_131/lib\:/usr/java/jdk1.8.0_131/jre/lib
java.vm.vendor=AdoptOpenJDK
sun.arch.data.model=64
java.vendor.url=https\://adoptopenjdk.net/
user.timezone=Asia/Shanghai
java.vm.specification.version=11
os.name=Linux
sun.java.launcher=SUN_STANDARD
user.country=CN
sun.boot.library.path=/usr/java/jdk-11.0.7+10/lib
sun.java.command=czhao.study.jvm.TestCase01GCTest testParam
jdk.debug=release
sun.cpu.endian=little
user.home=/home/zhaochun
user.language=zh
java.specification.vendor=Oracle Corporation
java.version.date=2020-04-14
java.home=/usr/java/jdk-11.0.7+10
file.separator=/
java.vm.compressedOopsMode=32-bit
line.separator=\n
java.vm.specification.vendor=Oracle Corporation
java.specification.name=Java Platform API Specification
java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
java.runtime.version=11.0.7+10
user.name=zhaochun
path.separator=\:
os.version=4.15.0-20-generic
java.runtime.name=OpenJDK Runtime Environment
file.encoding=UTF-8
java.vm.name=OpenJDK 64-Bit Server VM
java.vendor.version=AdoptOpenJDK
java.vendor.url.bug=https\://github.com/AdoptOpenJDK/openjdk-support/issues
java.io.tmpdir=/tmp
java.version=11.0.7
user.dir=/home/work/sources/test/study-jvm/out/production/study-jvm
os.arch=amd64
java.vm.specification.name=Java Virtual Machine Specification
java.awt.printerjob=sun.print.PSPrinterJob
sun.os.patch.level=unknown
java.library.path=/usr/java/packages/lib\:/usr/lib64\:/lib64\:/lib\:/usr/lib
java.vendor=AdoptOpenJDK
java.vm.info=mixed mode
java.vm.version=11.0.7+10
sun.io.unicode.encoding=UnicodeLittle
java.class.version=55.0
VM Flags:
-XX:CICompilerCount=4 -XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=8 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=20971520 -XX:MarkStackSize=4194304 -XX:MaxGCPauseMillis=200 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MinHeapDeltaBytes=1048576 -XX:NewSize=10485760 -XX:NonNMethodCodeHeapSize=5836300 -XX:NonProfiledCodeHeapSize=122910970 -XX:ProfiledCodeHeapSize=122910970 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC
VM Arguments:
jvm_args: -Xms20M -Xmx20M -Xmn10M -XX:+UseG1GC -XX:MaxGCPauseMillis=200
java_command: czhao.study.jvm.TestCase01GCTest testParam
java_class_path (initial): .:/usr/java/jdk1.8.0_131/lib:/usr/java/jdk1.8.0_131/jre/lib
Launcher Type: SUN_STANDARD
jinfo的输入内容有三局部,别离是:
Java System Properties
:JVM运行时的环境变量VM Flags
:失效的虚拟机参数配置VM Arguments
:启动JVM时传入的参数、命令、及过后的对应会话的环境变量
这里要尤其留神的是,如果零碎环境上不止一个JDK,比方这里的例子,理论有两个JDK,一个Java8一个Java11,那么运行JVM时应用的JDK版本应该看Java System Properties
里的java.runtime.version
,而不是VM Arguments
里的java_class_path (initial)
。
能够应用jinfo -sysprops <pid>
只查看JVM运行时的环境变量。
能够应用jinfo -flags <pid>
只查看失效的虚拟机参数配置,比方jinfo -flags 10517
。
能够应用jinfo -flag <name> <pid>
查看指定参数的值,比方jinfo -flag MaxHeapSize 10517
。
能够应用jinfo -flag [+|-]<name> <pid>
实时开启或敞开某个能够在运行时扭转的参数,比方jinfo -flag +HeapDumpOnOutOfMemoryError 10517
。
能够应用jinfo -flag <name>=<value>
实时批改某个能够在运行时扭转的参数的值,比方jinfo -flag MaxHeapFreeRatio=75 10517
。
通过命令
java -XX:+PrintFlagsFinal <pid> | grep manageable
查看哪些参数能够在运行时扭转。
1.4 jmap
jmap是java内存映像工具,次要用于查问以后堆和办法区的详细信息,生成堆的快照文件等。个别都是应用-XX:+HeapDumpOnOutOfMemoryError
参数指定JVM在内存溢出异样时主动生成堆的快照文件。之后在服务器产生内存溢出异样时,将对应的快照文件拉取到本地应用工具剖析。
jmap的堆转储快照文件有很多工具能够剖析,罕用的有MAT,Jprofiler,IBM HeapAnalyzer等,后续章节会一一介绍。
同jinfo一样,jmap在应用时,也要留神用户和JDK版本是否与指标JVM统一。
能够应用jmap -histo[:live] <pid>
剖析以后对中对象,例如./jmap -histo:live 10517
。
能够应用jmap -dump:[live,]format=b,file=<file> <pid>
对指标JVM进行快照转储,例如:
# 只转贮存活对象
./jmap -dump:live,format=b,file=/home/work/sources/test/study-jvm/output/jmap/dump001 10517
# 转储所有对象
./jmap -dump:format=b,file=/home/work/sources/test/study-jvm/output/jmap/dump002 10517
1.5 jhat
jhat是一个原始简陋的用来剖析jmap的堆转储快照文件的工具,Java9之后曾经被移出JDK的工具包。这里理解一下即可。
咱们用Java8的jhat工具对方才jmap转储的快照文件进行剖析:
zhaochun@zhaochun-T480:jmap$ jhat dump001
Reading from dump001...
Dump file created Mon Feb 08 12:37:58 CST 2021
Snapshot read, resolving...
Resolving 28806 objects...
Chasing references, expect 5 dots.....
Eliminating duplicate references.....
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
而后拜访http://localhost:7000/
就能够看到这个简陋的剖析页面。
1.6 jstack
jstack用于生成虚拟机以后时刻的线程快照。生成线程快照次要是为了定位长时间进展的线程,比方线程间死锁、死循环、申请内部资源超时等等。通过jstack能够查看到各个线程的调用堆栈信息,就能够晓得线程目前运行在哪一句代码,在做什么事件或者期待什么资源。
同样的,jstack应用时,也要留神用户、版本是否与指标JVM统一。
应用示例:
zhaochun@zhaochun-T480:bin$ jstack 3031
2021-02-08 15:11:01
Full thread dump OpenJDK 64-Bit Server VM (11.0.7+10 mixed mode):
Threads class SMR info:
_java_thread_list=0x00007fd8500b8430, length=12, elements={
0x00007fd8d0016000, 0x00007fd8d026b800, 0x00007fd8d026f800, 0x00007fd8d0284800,
0x00007fd8d0286800, 0x00007fd8d0288800, 0x00007fd8d028a800, 0x00007fd8d030f000,
0x00007fd8d0320800, 0x00007fd878001000, 0x00007fd86417b800, 0x00007fd850042800
}
"main" #1 prio=5 os_prio=0 cpu=135.52ms elapsed=2030.68s tid=0x00007fd8d0016000 nid=0xbd8 runnable [0x00007fd8d987a000]
java.lang.Thread.State: RUNNABLE
at java.io.FileInputStream.readBytes(java.base@11.0.7/Native Method)
at java.io.FileInputStream.read(java.base@11.0.7/FileInputStream.java:279)
at java.io.BufferedInputStream.read1(java.base@11.0.7/BufferedInputStream.java:290)
at java.io.BufferedInputStream.read(java.base@11.0.7/BufferedInputStream.java:351)
- locked <0x000000062ac01148> (a java.io.BufferedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(java.base@11.0.7/StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(java.base@11.0.7/StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(java.base@11.0.7/StreamDecoder.java:178)
- locked <0x000000062ac01a78> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(java.base@11.0.7/InputStreamReader.java:185)
at java.io.Reader.read(java.base@11.0.7/Reader.java:189)
at java.util.Scanner.readInput(java.base@11.0.7/Scanner.java:882)
at java.util.Scanner.findWithinHorizon(java.base@11.0.7/Scanner.java:1796)
at java.util.Scanner.nextLine(java.base@11.0.7/Scanner.java:1649)
at czhao.study.jvm.TestCase01GCTest.main(TestCase01GCTest.java:15)
"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=0.72ms elapsed=2030.63s tid=0x00007fd8d026b800 nid=0xbe0 waiting on condition [0x00007fd8a065e000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.7/Native Method)
at java.lang.ref.Reference.processPendingReferences(java.base@11.0.7/Reference.java:241)
at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.7/Reference.java:213)
"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.37ms elapsed=2030.63s tid=0x00007fd8d026f800 nid=0xbe1 in Object.wait() [0x00007fd8a055d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(java.base@11.0.7/Native Method)
- waiting on <0x000000062ac03180> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x000000062ac03180> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:176)
at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.7/Finalizer.java:170)
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.55ms elapsed=2030.63s tid=0x00007fd8d0284800 nid=0xbe2 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 cpu=5100.91ms elapsed=2030.63s tid=0x00007fd8d0286800 nid=0xbe3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"C1 CompilerThread0" #8 daemon prio=9 os_prio=0 cpu=1492.77ms elapsed=2030.63s tid=0x00007fd8d0288800 nid=0xbe4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"Sweeper thread" #9 daemon prio=9 os_prio=0 cpu=26.09ms elapsed=2030.63s tid=0x00007fd8d028a800 nid=0xbe5 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #10 daemon prio=9 os_prio=0 cpu=12.23ms elapsed=2030.60s tid=0x00007fd8d030f000 nid=0xbe6 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Common-Cleaner" #11 daemon prio=8 os_prio=0 cpu=3.01ms elapsed=2030.60s tid=0x00007fd8d0320800 nid=0xbe8 in Object.wait() [0x00007fd893167000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(java.base@11.0.7/Native Method)
- waiting on <0x000000062ac047a8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x000000062ac047a8> (a java.lang.ref.ReferenceQueue$Lock)
at jdk.internal.ref.CleanerImpl.run(java.base@11.0.7/CleanerImpl.java:148)
at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
at jdk.internal.misc.InnocuousThread.run(java.base@11.0.7/InnocuousThread.java:134)
"Attach Listener" #12 daemon prio=9 os_prio=0 cpu=276.95ms elapsed=2027.53s tid=0x00007fd878001000 nid=0xc0a waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"RMI TCP Accept-0" #14 daemon prio=9 os_prio=0 cpu=3.55ms elapsed=2026.03s tid=0x00007fd86417b800 nid=0xc16 runnable [0x00007fd89202d000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(java.base@11.0.7/Native Method)
at java.net.AbstractPlainSocketImpl.accept(java.base@11.0.7/AbstractPlainSocketImpl.java:458)
at java.net.ServerSocket.implAccept(java.base@11.0.7/ServerSocket.java:565)
at java.net.ServerSocket.accept(java.base@11.0.7/ServerSocket.java:533)
at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(jdk.management.agent@11.0.7/LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(java.rmi@11.0.7/TCPTransport.java:394)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(java.rmi@11.0.7/TCPTransport.java:366)
at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
"RMI Scheduler(0)" #16 daemon prio=9 os_prio=0 cpu=4.74ms elapsed=2025.98s tid=0x00007fd850042800 nid=0xc19 waiting on condition [0x00007fd891e2b000]
java.lang.Thread.State: WAITING (parking)
at jdk.internal.misc.Unsafe.park(java.base@11.0.7/Native Method)
- parking to wait for <0x000000062ac02540> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(java.base@11.0.7/LockSupport.java:194)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@11.0.7/AbstractQueuedSynchronizer.java:2081)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.7/ScheduledThreadPoolExecutor.java:1170)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.7/ScheduledThreadPoolExecutor.java:899)
at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@11.0.7/ThreadPoolExecutor.java:1054)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.7/ThreadPoolExecutor.java:1114)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.7/ThreadPoolExecutor.java:628)
at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
"VM Thread" os_prio=0 cpu=194.73ms elapsed=2030.64s tid=0x00007fd8d0263000 nid=0xbde runnable
"GC Thread#0" os_prio=0 cpu=98.26ms elapsed=2030.68s tid=0x00007fd8d0040800 nid=0xbd9 runnable
"GC Thread#1" os_prio=0 cpu=7.07ms elapsed=847.66s tid=0x00007fd888001000 nid=0x466f runnable
"GC Thread#2" os_prio=0 cpu=7.05ms elapsed=847.66s tid=0x00007fd888002000 nid=0x4670 runnable
"GC Thread#3" os_prio=0 cpu=7.40ms elapsed=847.66s tid=0x00007fd888003800 nid=0x4671 runnable
"GC Thread#4" os_prio=0 cpu=2.72ms elapsed=847.66s tid=0x00007fd888005000 nid=0x4672 runnable
"GC Thread#5" os_prio=0 cpu=5.62ms elapsed=847.66s tid=0x00007fd888006800 nid=0x4673 runnable
"GC Thread#6" os_prio=0 cpu=7.15ms elapsed=847.66s tid=0x00007fd888008000 nid=0x4674 runnable
"GC Thread#7" os_prio=0 cpu=1.41ms elapsed=847.66s tid=0x00007fd88800a000 nid=0x4675 runnable
"G1 Main Marker" os_prio=0 cpu=1.09ms elapsed=2030.67s tid=0x00007fd8d007a800 nid=0xbda runnable
"G1 Conc#0" os_prio=0 cpu=0.19ms elapsed=2030.67s tid=0x00007fd8d007c800 nid=0xbdb runnable
"G1 Refine#0" os_prio=0 cpu=0.91ms elapsed=2030.67s tid=0x00007fd8d01fe000 nid=0xbdc runnable
"G1 Young RemSet Sampling" os_prio=0 cpu=367.00ms elapsed=2030.67s tid=0x00007fd8d01ff800 nid=0xbdd runnable
"VM Periodic Task Thread" os_prio=0 cpu=1578.99ms elapsed=2030.60s tid=0x00007fd8d0311800 nid=0xbe7 waiting on condition
JNI global refs: 38, weak refs: 0
jstack反对的可选参数:
-F
:强制输入线程堆栈-l
:额定输入锁的附加信息-m
:如果调用了本地办法,输入C/C++堆栈
1.7 jcmd
jcmd提供了上述命令行工具的对立应用形式,如下表所示:
与jcmd具备相似性能的还有
jhsdb
,也一起列在上面的表中。jhsdb
还提供了图形化性能,在下一节介绍。
命令 | jcmd命令 | jhsdb命令 |
---|---|---|
jps -lm | jcmd | – |
jmap -dump <pid> | jcmd <pid> GC.heap_dump | jhsdb jmap –binaryheap –pid <pid> |
jmap -histo <pid> | jcmd <pid> GC.class_histogram | jhsdb jmap –histo –pid <pid> |
jstack <pid> | jcmd <pid> Thread.print | jhsdb jstack –locks –pid <pid> |
jinfo -sysprops <pid> | jcmd <pid> VM.system_properties | jhsdb info –sysprops –pid <pid> |
jinfo -flags <pid> | jcmd <pid> VM.flags | jhsdb jinfo –flags –pid <pid> |
同样的,jcmd应用时,也要留神用户、版本是否与指标JVM统一。
1.7 jhsdb
jhsdb除了命令以外,还提供了图形化性能来监督剖析JVM情况。
jhsdb应用如下命令开启指标JVM的图形化剖析界面:
jhsdb hsdb --pid <pid>
同样的,jhsdb应用时,也要留神用户、版本是否与指标JVM统一。
关上后首先有一个线程窗口,展示以后的线程信息。
另外tools菜单下有很多性能按钮,比方Class Browser
能够查看所有类信息,Heap Parameters
能够输入以后堆内存分区应用状况就像上面这样:
G1 Heap:
regions = 20
capacity = 20971520 (20.0MB)
used = 15204416 (14.50006103515625MB)
free = 5767104 (5.49993896484375MB)
72.50030517578125% used
G1 Young Generation:
Eden Space:
regions = 0
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
Survivor Space:
regions = 1
capacity = 1048576 (1.0MB)
used = 1048576 (1.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 14
capacity = 14680064 (14.0MB)
used = 14155840 (13.50006103515625MB)
free = 524224 (0.49993896484375MB)
96.42900739397321% used
还有很多其余性能,大家能够缓缓尝试。
1.8 jconsole
jconsole是一款对JVM的可视化监督管理工具,通过它咱们能够实时地监督JVM的内存、线程、类加载等信息的变化趋势。
应用上面的命令启动:
jconsole
同样的,jconsole应用时,也要留神用户、版本是否与指标JVM统一。
jconsole启动后有一个抉择JVM过程的界面,抉择一个JVM后即可看到界面。
如果要连贯一个近程机器上的JVM,那么须要在指标机器的JVM上减少以下参数:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.port=<port>
-Djava.rmi.server.hostname=<ip>
如果想要应用明码认证和ssl保障通信安全,请自行搜寻。
二、Java9后须要独自下载的JDK工具
Java9之后,局部JVM工具被移出了JDK工具包,须要独自去装置。这里介绍两个比拟好用的工具。
2.1 visualVM
visualVM是一个相似jconsole,然而比jconsole的性能更弱小丰盛的JVM监督工具,还能够用来剖析jmap的堆转储快照文件。它也能够连贯近程JVM,办法与jconsole的近程JVM一样。
visualVM的下载地址是:
https://visualvm.github.io/download.html
下载后启动:
./visualvm --jdkhome /usr/java/jdk-11.0.7+10
同样的,visualVM应用时,也要留神用户、JDK版本是否与指标JVM统一。
visualVM监督信息比jconsole更丰盛,而且它还能够间接导入jmap的堆转储快照文件,并依照你须要的维度做一些简略的排序展现。
2.2 JMC
JMC,Java Mission Control
,是另一个很弱小的JVM监督工具,和visualVM、jconsole一样,它能够监督JVM的各种数据。除此以外,它还提供了弱小的航行记录器
性能,记录一段时间内JVM的各种信息,包含内存、代码、线程、IO、事件等等的记录,而后基于这些信息做性能剖析。剖析后果如下图所示:
JMC下载地址如下:
https://www.oracle.com/java/technologies/javase/products-jmc7-downloads.html
本地启动命令:
./jmc
页面启动后,左侧会显示本地JVM,如果要连贯近程JVM,能够在文件 --> 连贯
菜单中创立新连贯。近程JVM的参数配置与jconsole一样。
间接点击左侧菜单的指标JVM即可关上实时监督页面,双击左侧菜单该JVM上层的航行记录器
即可开始一次航行记录,完结后会主动给出剖析报告。
Flight Recorder
航行记录,简称JFR
,以前是商业JDK的个性,起初在JDK11中开源,通常能够通过JVM启动参数-XX:StartFlightRecording
开启,或者通过jcmd
相干命令录制。这里通过JMC工具能够可视化录制航行记录。
JFR是一种用于收集对于正在运行的 Java 应用程序的诊断和剖析数据的工具。它集成到 Java 虚拟机(JVM)中,简直不会造成性能开销,因而即便在负载十分大的生产环境中也能够应用它。它收集JVM的各种事件信息,包含:磁盘 IO、GC、线程 sleep、线程 wait、Socket read/write 等。JFR就如同飞机上的黑匣子,通过收集的这些事件的详细信息可能更加深刻理解程序的外部运行过程,这是很多其余工具所不具备的。
除了实时JVM监督和航行记录剖析以外,JMC也能够间接关上并剖析jmap的堆转储快照文件,剖析后果如下:
三、其余工具
3.1 MAT
MAT,全称Memory Analyzer Tool
,它是一个傻瓜式的堆转储快照文件剖析工具,既能够本人生成堆转储快照文件,也能够间接剖析jmap命令导出的快照文件。
MAT工具能够提供以下剖析:
- Histogram:列出内存中的对象,对象的个数以及大小
- Dominator Tree:列出最大的对象以及其依赖存活的Object
- Top Consumers : 通过图形列出最大的object
- duplicate classes :检测由多个类装载器加载的类
- Leak Suspects :内存透露剖析
- Top Components:列出大于总堆数的百分之1的报表
- Component Report:剖析属于同一个包或者被同一个类加载器加载的对象情况
其中最罕用的就是Leak Suspects
内存透露剖析。
MAT须要独自下载安装,下载地址:
https://www.eclipse.org/mat/downloads.php
MAT是eclipse系的工具,相似与Jprofiler之于IDEA,但它是收费的,不像Jprofiler还免费。。。
下载到本地解压后间接运行MemoryAnalyzer
即可。
应用也很简略,首页点击Open heap dump
,而后抉择对应的堆转储快照文件即可。
咱们个别是在JVM启动参数增加-XX:+HeapDumpOnOutOfMemoryError
让JVM在产生内存溢出异样时主动dump堆快照文件,所以剖析时最重要的就是找出数量和空间耗费最大的对象信息,并通过调用堆栈信息查找可能产生内存泄露的代码所在。
而通过MAT的Leak Suspects
性能,就能间接给出一个内存泄露剖析报告,如下所示:
同时报告还会指出最可能产生泄露的具体对象及其调用堆栈信息:
更多相干信息请查看官网文档:
http://wiki.eclipse.org/Memor…
3.2 IBM HeapAnalyzer
MAT在剖析内存泄露时,尽管能疾速定位产生内存溢出异样时占用空间最多的对象,但这些对象往往是很底层的对象,咱们要通过堆栈调用去找到真正代码中产生泄露的中央。而MAT貌似没有直观展示调用关系树的性能,这时咱们能够应用IBM HeapAnalyzer
这个工具。
IBM HeapAnalyzer
的下载和应用参考上面的地址:
https://www.ibm.com/support/pages/node/1109955?mhsrc=ibmsearch_a&mhq=heapanalyzer
它与MAT一样,也能够主动做内存泄露剖析,比MAT更好的中央是,它对堆栈调用做了可视化的转换,能够更直观地看到调用关系树。
IBM HeapAnalyzer
最大的问题是,它曾经很久没有更新保护了。。。
3.3 Jprofiler
JProfiler 是由 ej-technologies 公司开发的一款性能瓶颈剖析工具。它是一款优良的商业软件,性能十分丰盛,因而具备一些免费软件所不具备的性能。Jprofiler 提供的次要性能有内存视图、CPU 视图、线程视图、堆遍历器(Heap Walker)等。
它与MAT,IBM HeapAnalyzer一样,能够用于剖析堆转储快照文件。但因为免费,这里就不介绍了。
3.3 Arthas
后面所有的JVM工具,都是基于JVM本人提供的MBeans/JMX技术,或者JFR技术去监督JVM状态。要么是监督JVM运行时数据,要么是OOM之后的堆转储快照文件的离线剖析。
如果咱们要找到一个JVM运行时的性能瓶颈所在,咱们须要监督运行时内存、线程、CPU等资源的变动,并找到对应时间段的对象或线程来定位具体比拟耗费资源的代码。这种形式实际操作起来还是很麻烦的。
而Arthas就是一个在实时监督跟踪具体方法方面特地弱小的一种JVM在线调试工具。Arthas提供了在线的办法级别的监督跟踪性能。比方monitor/watch/trace等指令,通过字节码加强技术,间接在代码字节码层面做插桩,实现对运行时办法调用链、耗时、返回数据等信息的动静实时监督。
Arthas的装置与应用能够间接参考官网文档,上手很简略:
https://arthas.aliyun.com/doc/index.html
https://arthas.aliyun.com/doc/quick-start.html
https://arthas.aliyun.com/doc/advanced-use.html
除了JMC的JFR航行记录,其余JVM工具所能监督的信息,基本上Arthas也都具备对应性能。而除了这些JVM信息查看、运行时情况监督。以及办法的实时监督跟踪之外,Arthas还有在线编译与反编译的性能,对于某些场景下的长期验证,或确认部署版本是否正确等非凡需要而言,非常不便。
但arthas在应用时,会对JVM的字节码造成入侵,会占用局部资源,对系统整体性能有肯定的影响。所以相比于其余工具,arthas其实是一个开发人员的调试工具,而不是JVM运维工具。
四、JVM工具总结
JVM运维与调试工具当然并不是仅仅只有本章节所列举的这些,但一般而言,这些工具把握局部也就满足平时的须要了。这里对它们进行一个简略的总结。
- 当你只是简略地查看JVM运行时的情况时,你能够间接应用JDK自带的那些工具命令,比方
jps
、jinfo
等等。 - 当你须要在OOM时查看内存泄露起因时,能够间接在JVM参数中配置OOM主动dump堆转储快照文件,并配合
jmap
等工具手动或定时周期性地dump堆快照。 - 当你想实时监督JVM的内存、线程、CPU等资源耗费趋势时,你能够应用
jconsole
、visualVM
、JMC
等工具。 - 当你想全面监督JVM各种事件信息,包含磁盘 IO、GC、线程 sleep、线程 wait、Socket read/write 等等,且不想对JVM性能带去影响时,你能够通过JMC录制JFR航行记录,并在JMC中查看报告。
- 当你须要在办法层面上监督跟踪其调用链路,耗时及返回值时,你能够应用arthas这样的在线JVM调试工具。
集体认为作为保底伎俩,无论是JVM运维还是开发调试,JDK自带的那些工具比方jps
、jinfo
、jstat
、jstack
等,咱们都应该要学习如何应用。
对于偏差于JVM运维的可视化监督方面,举荐应用JMC,并尝试录制JFR航行记录。对于业余的JVM运维,以及编写各种性能剖析报告来说,这个工具很有用。
而对于开发人员来说,目前最举荐的工具是arthas
,基本上JVM调试须要的性能它都有。
发表回复