jvm中常用的性能监控和调优工具介绍与使用
Posted 孔子-说
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm中常用的性能监控和调优工具介绍与使用相关的知识,希望对你有一定的参考价值。
目录
3.4 jmap:生成虚拟机的内存存储快照(heap dump文件)
1、引言
企业级Java开发中,有时候我们会碰到下面这些问题:
- OutOfMemoryError,内存不足
- 内存泄露
- 线程死锁
- 锁争用(Lock Contention)
- Java进程消耗CPU过高
- ......
这些问题在日常开发中可能被很多人忽视(比如有的人遇到上面的问题只是重启服务器或者调大内存,而不会深究问题根源),但能够理解并解决这些问题是Java程序员进阶的必备要求。本文将对一些常用的JVM性能调优监控工具进行介绍。
2、性能监控工具概述
工具主要是为了解决问题而生的,在JDK安装目录,就有很多这样的工具。
- 工具简介
工具名称 | 主要作用 |
---|---|
jps(JVM Process Status Tool) | 显示指定系统中所有的HotSpot虚拟机进程 |
jstat(JVM Statistics Monitoring Tool) | 手机HotSpot虚拟机各方面的运行数据 |
jinfo(Configuration Info for Java) | 显示虚拟机配置信息 |
jmap(Memory Map for Java) | 生成虚拟机的内存存储快照(heap dump文件) |
jhat(JVM Heap Dump Browser) | 分析内存存储快照,不推荐使用,消耗资源而且慢 |
jstack(Stack Trace for Java) | 显示虚拟机的线程快照 |
JConsole | JMX的可视化管理工具 |
VisualVM | 多合一故障管理工具 |
3、使用方式及应用
3.1 jps:虚拟机进程状况工具
jps主要用来输出JVM中运行的进程状态信息。
3.1.1 语法格式
jps [options] [hostid]
第一个参数:options
-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数
第二个参数:hostid
主机或者是服务器的id,如果不指定,就默认为当前的主机或者是服务器。
3.1.2 用例
3.2 jstat:虚拟机统计信息监控工具
jstat监视虚拟机各种运行状态信息,可以显示本地或者是远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
命令选项 | 涵义 |
-class | 监视类装载、卸载数量、总空间及类装载所耗费的时间 |
-gc | 监视java堆状况,包括Eden区、2个survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息 |
-gccapacity | 监视内容与-gc基本相同,但输出主要关注java堆各个区域使用到的最大和最小空间 |
-gcutil | 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 |
-gccause | 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因 |
-gcnew | 监视新生代GC的情况 |
-gcnewcapacity | 监视内容与-gcnew基本相同,输出主要关注使用到的最大和最小空间 |
-gcold | 监视老年代GC的情况 |
-gcoldcapacity | 监视内容与-gcold基本相同,输出主要关注使用到的最大和最小空间 |
-gcpermcapacity | 输出永久代使用到的最大和最小空间(java8之前版本) |
-gcmetacapacity | 元数据空间统计(java8及之后版本) |
-compiler | 输出JIT编译期编译过的方法、耗时等信息 |
-printcompilation | 输出已经被JIT编译的方法 |
3.2.1 语法格式
jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]
jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]
jstat -class 3176 1000 10(每隔1s,共输出10次)
第一个参数:generalOption | outputOptions
这个参数表示的option,代表着用户希望查询的虚拟机信息,分为类加载、垃圾收集、运行期编译状况3类。
第二个参数:vmid
vmid是Java虚拟机ID,即当前运行的java进程号,在Linux/Unix系统上一般就是进程ID。
第三个参数:interval
interval是采样时间间隔,单位为秒或毫秒。
第四个参数:count
count表示的是采样数,即打印次数,如果缺省则打印无数次。
3.2.2 用例
- 1)-class 查看类加载信息
Loaded:加载class的数量
Bytes:所占用空间大小
Unloaded:未加载数量
Bytes:未加载占用空间
Time:时间
- 2) -gcutil 总结垃圾回收统计
S0: 新生代中Survivor space 0区(幸存1区)已使用空间的百分比
S1: 新生代中Survivor space 1区(幸存2区)已使用空间的百分比
E: 新生代(伊甸园区)已使用空间的百分比
O: 老年代已使用空间的百分比
M:元数据区使用比例
CCS:压缩使用比例
YGC: 从应用程序启动到当前,发生Yang GC 的次数(年轻代垃圾回收次数)
YGCT: 从应用程序启动到当前,Yang GC所用的时间【单位秒】
FGC: 从应用程序启动到当前,发生Full GC的次数(老年代垃圾回收次数)
FGCT: 从应用程序启动到当前,Full GC所用的时间
GCT: 从应用程序启动到当前,用于垃圾回收的总时间【单位秒】
- 3) -gc 查看垃圾回收情况
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
- 4) -gccause 查看垃圾回收情况
S0 — Heap上的 Survivor space 0 区已使用空间的百分比
S1 — Heap上的 Survivor space 1 区已使用空间的百分比
E — Heap上的 Eden space 区已使用空间的百分比
O — Heap上的 Old space 区已使用空间的百分比
P — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
- 5) -gcnew 新生代垃圾回收统计
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
- 6) -gcold 老年代垃圾回收统计
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
- 7)-compiler 查看JIT编译信息
Compiled:编译数量。
Failed:失败数量
Invalid:不可用数量
Time:时间
FailedType:失败类型
FailedMethod:失败的方法
- 8) -gccapacity 堆内存统计
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数
- 9)-gcnewcapacity 新生代内存统计
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数
- 10)-gcoldcapacity 老年代内存统计
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
- 11)-gcmetacapacity 元数据空间统计
MCMN: 最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
- 12)-printcompilation JVM编译方法统计
Compiled:最近编译方法的数量
Size:最近编译方法的字节码数量
Type:最近编译方法的编译类型。
Method:方法名标识。
3.2.3 jstat的局限性
没有提供有关GC活动的丰富详细信息。 例如,从jstat中您将不知道:
- 如果一次样本中报告了多个GC事件,则我们将不知道每个GC事件的暂停时间是多少。
- 用户(即Java层),系统(即内核)和用户花费了多少时间。
- 有多少个GC线程正在工作,并占用了多少时间?
- 一个GC事件具有几个子阶段(例如初始标记,清理,备注,并发标记……)。 无法提供信息分类。
- 每个GC事件回收多少字节。
- 有时,jstat报告的数据也会产生误导 。
如果您想进行准确的GC分析,GC日志是更可靠的方法。
3.3 jinfo:显示虚拟机配置信息
jinfo(Configuration Info for Java),显示虚拟机配置信息,实时地查看和调整虚拟机各项参数。
3.3.1 语法格式
jinfo [option] pid
第一个参数:option
主要选项:
no option 输出全部的参数和系统属性
-flag name 输出对应名称的参数
-flag [+|-]name 开启或者关闭对应名称的参数
-flag name=value 设定对应名称的参数
-flags 输出全部的参数
-sysprops 输出系统属性
第二个参数:pid
指定显示的进程id。
3.3.2 用例
- 1) jinfo pid
输出当前 jvm 进程的全部参数和系统属性。
- 2) jinfo -flag name pid
输出对应名称的参数。
- 3) jinfo -flag [+|-]name pid
开启或者关闭对应名称的参数,使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。
- 4) jinfo -flag name=value pid
修改指定参数的值。同示例三,但示例三主要是针对 boolean 值的参数设置的。如果是设置 value值,则需要使用 name=value 的形式。jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改。
- 5) jinfo -flags pid
输出全部的参数。
- 6) jinfo -sysprops pid
输出当前 jvm 进行的全部的系统属性。
3.4 jmap:生成虚拟机的内存存储快照(heap dump文件)
jmap(Memory Map for Java),内存映像工具,用于生成堆转存的快照,一般是heapdump或者dump文件。如果不使用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError参数,当虚拟机发生内存溢出的时候可以产生快照。或者使用kill -3 pid也可以产生。jmap的作用并不仅仅是为了获取dump文件,也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列,java堆和永久代的详细信息,如空间使用率,当前用的哪种收集器。
3.4.1 语法格式
jmap [option] vmid
option: 选项参数。
pid: 需要打印配置信息的进程ID。
executable: 产生核心dump的Java可执行文件。
core: 需要打印配置信息的核心文件。
server-id 可选的唯一id,如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。
remote server IP or hostname 远程调试服务器的IP地址或主机名。
option
no option: 查看进程的内存映像信息,类似 Solaris pmap 命令。
heap: 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等,只在Linux/Solaris平台下有效。
histo[:live]: 显示堆中对象的统计信息,包括类、实例数量、合计容量。
clstats:打印类加载器信息
finalizerinfo: 显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象,只在Linux/Solaris平台下有效。
dump:<dump-options>:生成java堆转储快照,格式为-dump:[live, ]format=b, file=<filename>, 其中live子参数说明是否只dump出存活的对象。
permstat: 以ClassLoader为统计口径显示永久代内存状态,只在Linux/Solaris平台下有效。
F: 当虚拟机进程对-dump没有响应时,可使用这个选项强制生成dump快照。使用-dump或者-histo参数. 在这个模式下,live子参数无效.
help:打印帮助信息
J<flag>:指定传递给运行jmap的JVM的参数
3.4.2 用例
- 1) jmap pid
查看进程的内存映像信息,类似 Solaris pmap 命令。使用不带选项参数的jmap打印共享对象映射,将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。
- 2) jmap -heap pid
显示Java堆详细信息。打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息。
C:\\Users\\server>jmap -heap 14268
Attaching to process ID 14268, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.111-b14
using thread-local object allocation.
Parallel GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 5368709120 (5120.0MB)
NewSize = 2147483648 (2048.0MB)
MaxNewSize = 2147483648 (2048.0MB)
OldSize = 3221225472 (3072.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 524288000 (500.0MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 792723456 (756.0MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 2145386496 (2046.0MB)
used = 1743475808 (1662.7080993652344MB)
free = 401910688 (383.2919006347656MB)
81.26628051638487% used
From Space:
capacity = 1048576 (1.0MB)
used = 786432 (0.75MB)
free = 262144 (0.25MB)
75.0% used
To Space:
capacity = 1048576 (1.0MB)
used = 0 (0.0MB)
free = 1048576 (1.0MB)
0.0% used
PS Old Generation
capacity = 3221225472 (3072.0MB)
used = 416272888 (396.98876190185547MB)
free = 2804952584 (2675.0112380981445MB)
12.922811259826025% used
31993 interned Strings occupying 3071760 bytes.
- 3) jmap -histo:live pid
显示堆中对象的统计信息。其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项,则只计算活动的对象。
采用jmap -histo pid>a.log日志将其保存,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。
jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具)。
class name是对象类型,说明如下:
B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象
- 4) jmap -clstats pid
打印类加载器信息。-clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据,打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外,包含的字符串数量和大小也会被打印。
- 5) jmap -finalizerinfo pid
打印等待终结的对象信息。Number of objects pending for finalization: 0 说明当前F-QUEUE队列中并没有等待Fializer线程执行final。
6) jmap -dump:format=b,file=heapdump.phrof pid
生成堆转储快照dump文件。以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
这个命令执行,JVM会将整个heap的信息dump写入到一个文件,heap如果比较大的话,就会导致这个过程比较耗时,并且执行的过程中为了保证dump的信息是可靠的,所以会暂停应用, 线上系统慎用。
3.5 jhat:分析内存存储快照
jhat(JVM Heap Dump Browser/Java Heap Analysis Tool) ,是java虚拟机自带的一种虚拟机堆转储快照分析工具,不推荐使用,消耗资源而且慢。
可以用jhat命令将dump出来的hprof文件转成html的形式,然后通过http访问可以查看堆情况。
通过浏览器访问http://localhost:7000则可以看到如下信息,显示jvm中所有非平台类信息。通过这些连接可以进一步查看所有类信息(包括JAVA平台的类)、所有类的实例数量以及实例的基本信息。最后,还有一个连接指向OQL查询页面。
3.5.1 语法格式
jhat C:\\Users\\server\\heapdump.phrof
phrof 文件路径
3.5.2 用例
这样就启动起来了一个简易的HTTP服务,端口号是7000,尝试一下用浏览器访问一下它,本地的可以通过http://localhost:7000,就可以得到这样的页面:
All classes including platform 把所有类信息显示出来(默认是不包括Java平台的类)
查看堆异常主要关注两个:
Show instance counts for all classes (excluding platform) 平台外的所有对象信息
Show heap histogram 显示堆的统计信息
3.6 jstack:显示虚拟机的线程快照
jstack(Stack Trace for Java) ,主要用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
3.6.1 语法格式
$jstack [ option ] pid
$jstack [ option ] executable core
$jstack [ option ] [server-id@]remote-hostname-or-IP
参数说明:
pid: java应用程序的进程号,一般可以通过jps来获得;
executable:产生core dump的java可执行程序;
core:打印出的core文件;
remote-hostname-or-ip:远程debug服务器的名称或IP;
server-id: 唯一id,假如一台主机上多个远程debug服务;
3.6.2 用例
- 查看输出 jstack –l pid
通过jstack输出的线程信息主要包括:jvm自身线程、用户线程等。其中jvm线程会在jvm启动时就会存在。对于用户线程则是在用户访问时才会生成。
1)jvm线程:
在线程中,有一些 JVM内部的后台线程,来执行譬如垃圾回收,或者低内存的检测等等任务,这些线程往往在JVM初始化的时候就存在,如下所示:
"Attach Listener" daemon prio=10 tid=0x0000000052fb8000 nid=0xb8f waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
destroyJavaVM" prio=10 tid=0x00002aaac1225800 nid=0x7208 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
2)用户级别的线程
还有一类线程是用户级别的,它会根据用户请求的不同而发生变化。该类线程的运行情况往往是我们所关注的重点。而且这一部分也是最容易产生死锁的地方。
"qtp496432309-42" prio=10 tid=0x00002aaaba2a1800 nid=0x7580 waiting on condition [0x00000000425e9000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000788cfb020> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
at org.eclipse.jetty.util.BlockingArrayQueue.poll(BlockingArrayQueue.java:320)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:479)
at java.lang.Thread.run(Thread.java:662)
Locked ownable synchronizers:
- None
从上述的代码示例中我们可以看到该用户线程的以下几类信息:
Ø 线程的状态:waiting on condition(等待条件发生)
Ø 线程的调用情况;
Ø 线程对资源的锁定情况:Locked
- jstack检测死锁
1)死锁代码
public class DeadLock
private static Object objA = new Object();
private static Object objB = new Object();
public static void main(String[] args)
Thread thread1 = new Thread(new Thread1());
Thread thread2 = new Thread(new Thread2());
thread1.start();
thread2.start();
private static class Thread1 implements Runnable
@Override
public void run()
synchronized (objA)
System.out.println("线程1得到A对象的锁");
try
Thread.sleep(3000);
catch (InterruptedException e)
e.printStackTrace();
synchronized (objB)
System.out.println("线程1得到B对象的锁");
private static class Thread2 implements Runnable
@Override
public void run()
synchronized (objB)
System.out.println("线程2得到B对象的锁");
try
Thread.sleep(3000);
catch (InterruptedException e)
e.printStackTrace();
synchronized (objA)
System.out.println("线程2得到A对象的锁");
2)运行结果只能看到两个线程各只拿到了一个锁,在一直等待对方的锁释放。
线程1得到A对象的锁
线程2得到B对象的锁
3)使用 jps 来查看对应的 PID ,然后使用 jstack 来查看其线程情况:
C:\\Users\\server>jps
4240 Jps
480 DeadLock
C:\\Users\\server>jstack 480
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.45-b08 mixed mode):
"DestroyJavaVM" prio=6 tid=0x00000000047c1000 nid=0x9878 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" prio=6 tid=0x0000000010aa3000 nid=0xafa0 waiting for monitor entry [0x000000001105f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.zaimeibian.Test$Thread2.run(Test.java:46)
- waiting to lock <0x00000007c099cc20> (a java.lang.Object)
- locked <0x00000007c099cc30> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
"Thread-0" prio=6 tid=0x0000000010aa2800 nid=0xae74 waiting for monitor entry [0x0000000010f5f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.zaimeibian.Test$Thread1.run(Test.java:27)
- waiting to lock <0x00000007c099cc30> (a java.lang.Object)
- locked <0x00000007c099cc20> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
"Service Thread" daemon prio=6 tid=0x000000000f10a000 nid=0x9a8c runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=10 tid=0x000000000f109800 nid=0xaf28 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=10 tid=0x000000000f105800 nid=0x85dc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" daemon prio=10 tid=0x000000000f104800 nid=0xac04 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x000000000f102000 nid=0xa678 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=8 tid=0x000000000f0bd000 nid=0xaed8 in Object.wait() [0x000000001045f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007c0905568> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000007c0905568> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)
"Reference Handler" daemon prio=10 tid=0x000000000f0b2000 nid=0xaedc in Object.wait() [0x000000001035f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007c09050f0> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000007c09050f0> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=10 tid=0x000000000f0b0000 nid=0xaef0 runnable
"GC task thread#0 (ParallelGC)" prio=6 tid=0x00000000047d6000 nid=0xacb0 runnable
"GC task thread#1 (ParallelGC)" prio=6 tid=0x00000000047d8000 nid=0xaee0 runnable
"GC task thread#2 (ParallelGC)" prio=6 tid=0x00000000047d9800 nid=0xaed4 runnable
"GC task thread#3 (ParallelGC)" prio=6 tid=0x00000000047db000 nid=0xac54 runnable
"VM Periodic Task Thread" prio=10 tid=0x000000000f132000 nid=0xaff0 waiting on condition
JNI global references: 105
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000000f0ba488 (object 0x00000007c099cc20, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000000f0bcf28 (object 0x00000007c099cc30, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.zaimeibian.Test$Thread2.run(Test.java:46)
- waiting to lock <0x00000007c099cc20> (a java.lang.Object)
- locked <0x00000007c099cc30> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
"Thread-0":
at com.zaimeibian.Test$Thread1.run(Test.java:27)
- waiting to lock <0x00000007c099cc30> (a java.lang.Object)
- locked <0x00000007c099cc20> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:744)
Found 1 deadlock.
可以看到 jstack 打印出了线程的状态,而且发现一个死锁。另外,线程状态有以下几种:
- RUNNABLE 线程运行中或 I/O 等待
- BLOCKED 线程在等待 monitor 锁( synchronized 关键字)
- TIMED_WAITING 线程在等待唤醒,但设置了时限
- WAITING 线程在无限等待唤醒
jstack详细用法见 jstack和线程dump分析_熠熠的专栏-CSDN博客
3.7 JConsole:JMX的可视化管理工具
JConsole,JMX的可视化管理工具。这个工具相比较前面几个工具,使用率比较高,很重要。它是一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。
在cmd里面输入 jconsole,选则进程就可以了。(也可以在jdk安装bin目录下找到jconsole.exe执行)。
当JConsole成功建立连接,它从连接上的JMX代理处获取信息,并且以下面几个标签页呈现信息。
- Summary tab(概览). 监控JVM和一些监控变量的信息。
- Memory tab(内存). 内存使用信息
- Threads tab(线程). 线程使用信息
- Classes tab(类). 类调用信息
- VM tab(VM概要). JVM的信息
- MBeans tab(MBean).所有MBeans的信息
3.7.1 概览
概述选项卡中显示CPU使用率,内存使用率,线程数,Java VM中加载的类的监控信息。
3.7.2 内存
监控内存消耗,提供了内存消耗和内存池的信息,相当于可视化的jstat命令。
内存标签功能“执行GC”的按钮,可以单击执行垃圾收集。 图表动态显示内存使用的堆和非堆内存的内存池。 可用的内存池取决于正在使用的版本的Java VM。 串行垃圾回收的内存池的HotSpot Java虚拟机,有以下几种。
- 伊甸园空间(堆):大多数对象最初分配内存的池。
- 生存空间(堆):包含伊甸园空间垃圾收集后生存的对象。
- 年老代(堆):池包含已经存在一段时间的对象。
- 永久代(非堆):池包含的所有虚拟机本身的反射的数据,如类和方法的对象。 Java虚拟机,使用类数据共享,这一代分为只读和读写区域。
- 代码缓存(非堆):HotSpot Java虚拟机的还包括一个代码缓存,包含内存,使用本机代码的编译和存储。
“详细信息”区域显示了当前内存信息:
- 已使用:目前使用的内存量,包括所有对象,可达和不可达占用的内存。
- 已提交-分配 :保证由Java虚拟机使用的内存量。 提交的内存量可能会随时间而改变。 Java虚拟机可能会释放系统内存,并已提交的内存量可能会少于最初启动时分配的内存量。 提交的内存量将始终大于或等于使用的内存量。
- 最大值,可用于内存管理的最大内存量。 它的价值可能会发生变化,或者是不确定的。 如果Java虚拟机试图增加使用的内存要大于提交的内存,内存分配可能失败,即使使用量小于或等于最大值(例如,当系统上的虚拟内存不足)。
- GC时间 :累计时间花在垃圾收集和调用的总数。 它可能有多个行,其中每一个代表一个垃圾收集器算法在Java虚拟机使用时间。
GC时会暂停整个JAVA应用,普通GC 只是对年轻代进行垃圾回收,full的GC会对整个堆内存(包含老年代、年轻代)进行垃圾回收。system的GC显示调用GC。
full gc 只会在两个情况下发生:1)system.gc被显示调用时,会执行full gc。2)老年代的堆内存满时,会执行full gc。
较低的右侧的条形图显示堆和非堆内存中的内存池消耗的内存。 列会变成红色时,使用的内存超过了内存使用阀值。
3.7.3 线程
线程“选项卡上提供了有关线程使用的信息。
在左下角的“线程”列表列出了所有的活动线程。 如果你输入一个“过滤器”字段中的字符串,线程列表将只显示其名称中包含你输入字符串线程。 点击一个线程在线程列表的名称,显示该线程的信息的权利,包括线程的名称,状态、阻塞和等待的次数、堆栈跟踪。
图表显示活动线程的数量随着时间的推移。 两行显示。
- 红色 :峰值线程数
- 蓝 :活动线程数。
检测死锁
要检查如果您的应用程序已经陷入了僵局运行(例如,您的应用程序似乎是挂了),死锁的线程可以通过点击“检测死锁”按钮检测。 如果检测到任何死锁的线程,这些都显示在一个新的死锁标签。
3.7.4 类
“类”标签显示关于类加载的信息。
图表曲线加载的类的数量随着时间的推移。
- 红线总数(包括后来卸载的)加载的类。
- 蓝线是当前的类加载。
在选项卡底部的详细信息部分显示类的加载,包括已加载当前类,已加载类总数、已卸载类总数。 跟踪类加载详细的输出,您可以勾选在顶部的右上角复选框。
3.7.5 VM概要
VM摘要“选项卡提供了对Java虚拟机的信息。
在此选项卡中提供的信息包括以下内容。
1)摘要
- 运行时间 :开始以来,Java虚拟机的时间总额。
- 进程的CPU时间 :Java VM的开始,因为它消耗的CPU时间总量。
- 编译总时间 :累计时间花费在JIT编译。
2)主题
- 活动线程 :目前现场守护线程,加上非守护线程数量。
- 峰值 :活动线程的最高数目,因为Java虚拟机开始。
- 守护线程 :当前的活动守护线程数量。
- 总线程 :开始自Java虚拟机启动的线程总数,包括非守护进程,守护进程和终止的线程。
3)类
- 当前类装载 :目前加载到内存中的类数目。
- 总类加载 :从Java VM开始加载到内存中的类总和,包括那些后来被卸载的类。
- 已卸载类总数 :从Java虚拟机开始从内存中卸载的类的数目。
4)内存
- 当前的堆大小 :目前所占用的堆的千字节数。
- 分配的内存 :堆分配的内存总量。
- 最大堆最大值 :堆所占用的千字节的最大数目。
- 待最后确定的对象:待最后确定的对象的数量。
- 花在执行GC的垃圾收集器 :包括垃圾收集,垃圾收集器的名称,进行藏品的数量和总时间的信息。
5)操作系统
- 总物理内存
- 空闲物理内存
- 分配的虚拟内存
6)其他信息
- VM参数 :输入参数的应用程序通过Java虚拟机,不包括的主要方法的参数。
- 类路径是由系统类加载器用于搜索类文件的类路径。
- 库路径 :加载库时要搜索的路径列表。
- 引导类路径 :引导类路径是由引导类加载器用于搜索类文件。
3.7.6 MBean
MBeans选项卡显示的信息平台MBean服务器中的一个通用的方法对所有已注册的MBean。 MBeans选项卡允许您访问平台MXBean。 此外,您还可以监控和管理您的应用程序的MBean。
左侧的树显示当前正在运行的所有MBean。 当您选择树中的一个MBean, 其 MBeanInfo及其MBean描述符都显示在右侧,并在它下面的树中出现的任何属性,操作或通知。
3.8 VisualVM:多合一故障管理工具
VisualVM,多合一故障管理工具。同jconsole都是一个基于图形化界面的、可以查看本地及远程的JAVA GUI监控工具,Jvisualvm同jconsole的使用方式一样,直接在命令行打入jvisualvm即可启动,jvisualvm界面更美观一些,数据更实时。(也可以在jdk安装bin目录下找到jvisualvm.exe执行)
VisualVM工具基本涵盖内存分析jmap、线程分析jstack等功能,并且是可视化的,可以远程连接生产环境的服务器进行分析。主要功能如下:
概述:显示JVM的一些基本信息。
监视:【执行垃圾回收】、【堆Dump】、CPU、内存、类、线程、Metaspace元数据空间。堆dump:类、实例数、OQL控制台。
线程:线程dump,类似于jstack
抽样器:对【CPU】和【内存】进行采样。
3.8.1 概述
显示JVM的一些基本信息。
3.8.2 监视
查看CPU、内存、类、线程运行信息。
3.8.3 线程
查看线程详细信息。
也可以点击右上角的dump按钮,将线程的信息导出,其实就是执行的jstack命令。
3.8.4 抽样器
抽样器可以对CPU、内存在一段时间内进行抽样,以供分析。
JVM监控和调优常用命令工具总结