简介
性能优化和故障排除是一件比较麻烦的事,针对性能优化,必须得先了解相关的优化工具,然后针对不同场景不同问题选用不同工具进行分析,找出故障原因;
jdk自带性能分析工具有:jps,jstack,jstat,jconsole,jmap和mat,visualvm;
linux常用性能分析工具有: top ,pidstat等
jps(jvm process status tools)
jps是用来显示当前所有java pid的命令工具,利用它可以用来查看java进程pid,以及main方法传递的参数,main方法的包名,以及运行时jvm配置参数:
- jps (null):jps 后面不跟任何参数表示查看当前系统中所有运行java的进程的pid;
- jps -l:查看运行java进程的main方法的完整的pacakge名称或者jar应用文件的完整路径名称;例如:
D:\java\jdk10\bin>jps -l
12912
48028 jdk.jcmd/sun.tools.jps.Jps
- jps -m:查看运行java进程的main方法传递进来的参数,如果没有就为null;
- jps -v:查看运行java进程启动配置的jvm运行时参数,例如:
D:\java\jdk10\bin>jps -v
12912 -Dosgi.requiredJavaVersion=1.8 [email protected]/eclipse-workspace -XX:+UseG1GC -XX:+UseStringDeduplication -Dosgi.requiredJavaVersion=1.8 -Dosgi.dataAreaRequiresExplicitInit=true -Xms256m -Xmx1024m -Declipse.p2.max.threads=10 -Doomph.update.url=http://download.eclipse.org/oomph/updates/milestone/latest -Doomph.redirection.index.redirection=index:/->http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/ -Dosgi.requiredJavaVersion=1.8 [email protected]/eclipse-workspace -XX:+UseG1GC -XX:+UseStringDeduplication -Dosgi.requiredJavaVersion=1.8 -Dosgi.dataAreaRequiresExplicitInit=true -Xms256m -Xmx1024m -Declipse.p2.max.threads=10 -Doomph.update.url=http://download.eclipse.org/oomph/updates/milestone/latest -Doomph.redirection.index.redirection=index:/->http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/
61372 Jps -Dapplication.home=D:\java\jdk10 -Xms8m -Djdk.module.main=jdk.jcmd
jstack(jvm stack tools)
jstack是jvm自带的可以用来生成运行时线程快照的工具,通过线程快照可以得到jvm运行时每一条运行中线程的信息,一般可以用来分析线程长时间停顿的原因,如死锁,死循环,激烈的锁竞争,外部资源请求等待,如下:
D:\java\jdk10\bin>jstack 34152
2019-05-23 23:20:34
Full thread dump Java HotSpot(TM) 64-Bit Server VM (11.0.2+7-LTS mixed mode):
Threads class SMR info:
_java_thread_list=0x000001fbfd43c300, length=11, elements={
0x000001fbfc995800, 0x000001fbfc996800, 0x000001fbfd298800, 0x000001fbfd29a800,
0x000001fbfd2a4000, 0x000001fbfd2a9000, 0x000001fbfd2c0800, 0x000001fbfd444000,
0x000001fbfd454000, 0x000001fbfd455800, 0x000001fbd9008800
}
"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=19.85s tid=0x000001fbfc995800 nid=0x57ac waiting on condition [0x0000003a388ff000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.Reference.waitForReferencePendingList([email protected]/Native Method)
at java.lang.ref.Reference.processPendingReferences([email protected]/Reference.java:241)
at java.lang.ref.Reference$ReferenceHandler.run([email protected]/Reference.java:213)
"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=19.85s tid=0x000001fbfc996800 nid=0x194e0 in Object.wait() [0x0000003a389ff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
- waiting on <0x0000000710d08f10> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x0000000710d08f10> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:176)
at java.lang.ref.Finalizer$FinalizerThread.run([email protected]/Finalizer.java:170)
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=19.82s tid=0x000001fbfd298800 nid=0x1364c runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=0.00ms elapsed=19.82s tid=0x000001fbfd29a800 nid=0x1fc0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 cpu=15.63ms elapsed=19.82s tid=0x000001fbfd2a4000 nid=0x11f28 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"C1 CompilerThread0" #9 daemon prio=9 os_prio=2 cpu=31.25ms elapsed=19.82s tid=0x000001fbfd2a9000 nid=0xf1bc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
No compile task
"Sweeper thread" #10 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=19.82s tid=0x000001fbfd2c0800 nid=0xcb58 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Service Thread" #11 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=19.78s tid=0x000001fbfd444000 nid=0xe398 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Common-Cleaner" #12 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=19.78s tid=0x000001fbfd454000 nid=0xeb30 in Object.wait() [0x0000003a391ff000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait([email protected]/Native Method)
- waiting on <0x0000000710c5ad28> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove([email protected]/ReferenceQueue.java:155)
- waiting to re-lock in wait() <0x0000000710c5ad28> (a java.lang.ref.ReferenceQueue$Lock)
at jdk.internal.ref.CleanerImpl.run([email protected]/CleanerImpl.java:148)
at java.lang.Thread.run([email protected]/Thread.java:834)
at jdk.internal.misc.InnocuousThread.run([email protected]/InnocuousThread.java:134)
"test" #13 prio=5 os_prio=0 cpu=19734.38ms elapsed=19.77s tid=0x000001fbfd455800 nid=0x63d8 runnable [0x0000003a392ff000]
java.lang.Thread.State: RUNNABLE
at jvm.optimize.TestOptimize$1.run(TestOptimize.java:11)
"DestroyJavaVM" #14 prio=5 os_prio=0 cpu=125.00ms elapsed=19.77s tid=0x000001fbd9008800 nid=0xb574 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=2 cpu=0.00ms elapsed=19.86s tid=0x000001fbfc983000 nid=0x6270 runnable
"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=19.89s tid=0x000001fbd901d800 nid=0x104fc runnable
"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=19.89s tid=0x000001fbd907b800 nid=0x9d20 runnable
"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=19.89s tid=0x000001fbd907e000 nid=0x1174c runnable
"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=19.88s tid=0x000001fbfc861800 nid=0xa648 runnable
"G1 Young RemSet Sampling" os_prio=2 cpu=0.00ms elapsed=19.88s tid=0x000001fbfc865000 nid=0xe880 runnable
"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=19.78s tid=0x000001fbfd44b000 nid=0x10dc0 waiting on condition
JNI global refs: 6, weak refs: 0
jstat
jstat是用来对基于Hotspot的jvm,对其堆的使用情况进行实时检测,如:
- 类的加载与卸载情况;
- 查看新生代,老年代,持久代堆内存容量及使用情况;
- 查看新生代,老年代,持久代垃圾回收情况,包括回收次数,每次回收消耗时间等;
- 查看新生代Eden区以及Survivor区分区情况;
例如:
D:\java\jdk10\bin>jstat -gc 68748
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 65536.0 7864.9 175104.0 0.0 4480.0 751.8 384.0 76.4 0 0.000 0 0.000 0.000
如上所示,各指标信息如下:
- S0C:表示Survivor 0区容量大小(字节)
- S1C:表示Survivor 1区容量大小(字节);
- S0U:表示Survivor 0区使用的容量大小;
- S1U:表示Survivor 1区使用的容量大小;
- EC:表示Eden区容量大小;
- EU:表示Eden区使用容量大小;
- OC:表示老年代容量大小;
- OU:表示老年代使用容量大小;
- MC:表示元数据区容量大小(JDK8以后出现的);
- MU:表示元数据区使用大小;
- CCSC:表示压缩类空间容量大小 (Compress Class Space Capacity);
- CCSU:表示压缩类空间使用大小;
- YGC:年轻代垃圾回收次数(Young GC Count);
- YGCT:年轻代垃圾回收消耗时间(Young GC Time);
- FGC:full gc垃圾回收次数;
- FGCT :full gc垃圾回收消耗时间(秒);
- GCT: GC TIMe,即垃圾回收消耗总时间=FGCT+YGCT;
jmap,mat,jhprof
jmap是用来打印或者导出某个jvm进程内所有对象(存活的或者死亡还未回收的);
jmap使用方法如下:
- jmap -heap pid:查看该进程下堆内存使用情况;
Heap Configuration:
MinHeapFreeRatio = 0//对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio = 100//对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize = 4183818240 (3990.0MB)//对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 87031808 (83.0MB) //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 1394606080 (1330.0MB) //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 175112192 (167.0MB) //对应jvm启动参数-XX:OldSize=:设置JVM堆的‘老生代’的大小
NewRatio = 2//对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8//对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
MetaspaceSize = 21807104 (20.796875MB) //对应jvm启动参数-XX:PermSize=:设置JVM堆的‘永生代’的初始大小
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB//对应jvm启动参数-XX:MaxPermSize=:设置JVM堆的‘永生代’的最大大小
G1HeapRegionSize = 0 (0.0MB)
Heap Usage://堆内存使用情况
PS Young Generation//年轻代
Eden Space://Eden区空间使用情况
capacity = 340787200 (325.0MB)
used = 306906696 (292.6890335083008MB)
free = 33880504 (32.31096649169922MB)
90.05816415640024% used
From Space://From Survivor区的内存使用情况
capacity = 524288 (0.5MB)
used = 491520 (0.46875MB)
free = 32768 (0.03125MB)
93.75% used
To Space://To Survivor区内存使用情况
capacity = 19922944 (19.0MB)
used = 0 (0.0MB)
free = 19922944 (19.0MB)
0.0% used
PS Old Generation //老年代内存使用情况
capacity = 219676672 (209.5MB)
used = 40968392 (39.07050323486328MB)
free = 178708280 (170.42949676513672MB)
18.649404885376267% used
- jmap -histo:live pid :查看指定进程下存活对象信息,可用于定位内存泄漏;
- jmap -histo:live pid >1.txt将信息输出到指定文件中
mat:是Eclipse的内存插件,可以用来查找内存泄漏和减少内存消耗;
jinfo
jinfo用于打印指定Java进程、核心文件或远程调试服务器的Java配置信息。配置信息包括Java系统属性、Java虚拟机命令行标识参数。
D:\java\jdk10\bin>jinfo 68748
Java System Properties:
#Fri May 24 01:03:18 CST 2019
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=D\:\\java\\jre1.8\\bin
java.vm.version=25.211-b12
java.vm.vendor=Oracle Corporation
java.vendor.url=http\://java.oracle.com/
path.separator=;
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
file.encoding.pkg=sun.io
user.script=
user.country=CN
sun.java.launcher=SUN_STANDARD
sun.os.patch.level=
java.vm.specification.name=Java Virtual Machine Specification
user.dir=D\:\\jar files
java.runtime.version=1.8.0_211-b12
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=D\:\\java\\jre1.8\\lib\\endorsed
os.arch=amd64
java.io.tmpdir=C\:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\
line.separator=\r\n
java.vm.specification.vendor=Oracle Corporation
user.variant=
os.name=Windows 10
sun.jnu.encoding=GBK
java.library.path=C\:\\Program Files (x86)\\Common Files\\Oracle\\Java\\javapath;C\:\\WINDOWS\\Sun\\Java\\bin;C\:\\WINDOWS\\system32;C\:\\WINDOWS;D\:\\dev tools\\apache-maven-3.6.1\\bin\\;C\:\\Program Files (x86)\\Common Files\\Oracle\\Java\\javapath;D\:\\Python37;D\:\\Python37\\Scripts;C\:\\Program Files (x86)\\NetSarang\\Xshell 6\\;D\:\\java\\jdk-11.0.2\\bin;C\:\\Windows\\system32;C\:\\Windows;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C\:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C\:\\WINDOWS\\system32;C\:\\WINDOWS;C\:\\WINDOWS\\System32\\Wbem;C\:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C\:\\WINDOWS\\System32\\OpenSSH\\;C\:\\Program Files\\Git\\cmd;C\:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR;C\:\\Go\\bin;D\:\\Program Files\\nodejs;D\:\\Program Files\\nodejs\\node_global;D\:\\Thrift;D\:\\protobuf\\protoc-3.4.0-win32\\bin;D\:\\apache-ant-1.10.6\\bin;D\:\\hadoop-3.1.2\\bin;D\:\\hadoop-3.1.2\\sbin;C\:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C\:\\Users\\Administrator\\AppData\\Roaming\\npm;.
java.specification.name=Java Platform API Specification
java.class.version=52.0
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
os.version=10.0
user.home=C\:\\Users\\Administrator
user.timezone=
java.awt.printerjob=sun.awt.windows.WPrinterJob
file.encoding=GBK
java.specification.version=1.8
java.class.path=testjvm.jar
user.name=Administrator
java.vm.specification.version=1.8
sun.java.command=testjvm.jar
java.home=D\:\\java\\jre1.8
sun.arch.data.model=64
user.language=zh
java.specification.vendor=Oracle Corporation
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.8.0_211
java.ext.dirs=D\:\\java\\jre1.8\\lib\\ext;C\:\\WINDOWS\\Sun\\Java\\lib\\ext
sun.boot.class.path=D\:\\java\\jre1.8\\lib\\resources.jar;D\:\\java\\jre1.8\\lib\\rt.jar;D\:\\java\\jre1.8\\lib\\sunrsasign.jar;D\:\\java\\jre1.8\\lib\\jsse.jar;D\:\\java\\jre1.8\\lib\\jce.jar;D\:\\java\\jre1.8\\lib\\charsets.jar;D\:\\java\\jre1.8\\lib\\jfr.jar;D\:\\java\\jre1.8\\classes
java.vendor=Oracle Corporation
sun.stderr.encoding=ms936
file.separator=\\
java.vendor.url.bug=http\://bugreport.sun.com/bugreport/
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.endian=little
sun.stdout.encoding=ms936
sun.desktop=windows
sun.cpu.isalist=amd64
VM Flags:
-XX:CICompilerCount=4 -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4280287232 -XX:MaxNewSize=1426587648 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
VM Arguments:
java_command: testjvm.jar
java_class_path (initial): testjvm.jar
Launcher Type: SUN_STANDARD
jconsole
jconsole使用java gui编写的图形界面可用于展示监视的java服务器,功能很强大,可以可视化的参看java服务器运行时线程,对象,内存等,如下图:
jhat(jvm heap analysis tool)
jhat是虚拟机堆转储快照分析工具,用于分析heapdump文件,可以建立http连接,让用户在浏览器上对堆进行分析;相对mat来说,jhat没有图形那么直观;
使用方法如下参考:
- 导出堆,如:jmap -dump:live,file=b.bin 81301
- 分析堆,如:jhat test.bin
- 查看堆,如:http://xxx.xxx:7000/
jcmd
jcmd功能很强大,可以用来导出堆,查看堆,导出线程,查看线程,查看进程信息,执行GC等;
查看java进程:
D:\java\jdk10\bin>jcmd
74512 jdk.jconsole/sun.tools.jconsole.JConsole
50184 jdk.jcmd/sun.tools.jcmd.JCmd
68748 testjvm.jar
可以使用help:
D:\java\jdk10\bin>jcmd 68748 help
68748:
The following commands are available:
JFR.stop
JFR.start
JFR.dump
JFR.check
VM.native_memory
VM.check_commercial_features
VM.unlock_commercial_features
ManagementAgent.stop
ManagementAgent.start_local
ManagementAgent.start
VM.classloader_stats
GC.rotate_log
Thread.print
GC.class_stats
GC.class_histogram
GC.heap_dump
GC.finalizer_info
GC.heap_info
GC.run_finalization
GC.run
VM.uptime
VM.dynlibs
VM.flags
VM.system_properties
VM.command_line
VM.version
help
通过help,可以看到能进行哪些操作,例如查看VM运行时间:
D:\java\jdk10\bin>jcmd 68748 VM.uptime
68748:
5334.977 s
JvisualVM(可视化vm分析工具)
该工具非常强大,可以以图形的方式对vm对象进行展示,快速分析,以及生成海量监控数据,并且可以分析GC趋势等等;
具体可以参考这篇文章:JvisualVM使用jp