JVM工具分析
JDK监控和故障处理工具
名称 | 主要作用 |
---|---|
jps | 查看系统内所有jvm虚拟机进程 |
jstat | 用于收集虚拟机各方面的运行数据 |
jinfo | 显示虚拟机配置信息 |
jmap | 生成虚拟机内存转储快照(dump 文件) |
jhat | 用于分析dump文件,会建立一个http服务,在7000端口 |
jstack | 显示虚拟机线程快照 |
处理工具详解
jps
常用选项
选项 | 作用 |
---|---|
-q | 只输出LVMID(jvm唯一进程id 一般与物理机进程相同),省略主类名称 |
-m | 输出虚拟机进程启动时传递给主类main()函数的参数 |
-l | 输出主类的全名,如果进程执行的是Jar包,输出jar路径 |
-v | 输出虚拟机进程启动时Jvm参数 |
详细举例
我本地启动了 IDEA开发工具,启动的idea项目采用了gradle做依赖管理。以此时的环境展示以下命令的结果。
- jps
jps
查看进程号,和main函数名称。
jps -q
查看进程号,省略Main函数名称。一般该命令不太常用,省略函数名称会导致区分不出你想要关注的进程。
jps -m
输出虚拟机进程启动时传递给主类main()函数的参数。
手动书写了一个main函数,使用Idea启动他,并在启动时设置program arguments。如下图所示,然后打断点,使主进程停止运行,防止线程结束。
然后使用Jps -m 可以查看此时的情况。
- jps -l 输出主类的全名,如果进程执行的是Jar包,输出jar路径。
打包了一个本地的springboot项目,项目名称为election_server。这个jar包的位置在target目录下并使用java -jar启动。使用jps -l命令查看
- jps -v 查看输出虚拟机进程启动时Jvm参数
启动了Idea,可以通过jps -v 查看idea启动的参数。本人电脑16G内存,为Idea最大内存分配了4000m,如图可以参数
注意:使用 jps -help可以查看所有命令的使用介绍, jps支持查看远程服务器的进程,但是不能查看某一具体进程的情况, 比如上图 2312是idea的进程id,使用 jps -v 2312这样是错误的命令
jstat
jstat(JVM Statistics Monitoring Tool) 用于监控虚拟机各种运行状态信息的命令行工具。
常用选项
选项 | 作用 |
---|---|
-class | 监视类装载、卸载数量、总空间以及类装载所耗费的时间 |
-gc | 监视Java堆状况,包括Eden区,两个survivor区、老年代、永久代等的容量、已用空间、GC合计等信息 |
-gccapacity | 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间 |
-gcutil | 监视内容与-gc基本相同,但输出占总空间的百分比 |
-gccause | 与-gcutil功能一样, 但是会额外输出导致上一次GC产生原因 |
-gcnew | 监视新生代GC状况 |
-gcnewcapacity | 监视内容与-gcnew相同,输出主要关注使用到的最大、最小空间 |
-gcold | 监视老年代GC状况 |
-gcoldcapacity | 监视内容与-gcold基本相同,输出主要关注使用到的最大、最小空间 |
-gcpermcapacity | 输出永久代使用到的最大、最小空间 |
-compiler | 输出JIT器编译过的方法、耗时等信息 |
-printcompilation | 输出已经被JIT编译的方法 |
详细举例
本地运行了一服务,先使用jps查看到该服务的进程号,然后使用jstat -option 查看信息.
名词解释:
幸存区1、幸存区2和eden区都属于jvm新生代。三者的比例按照1:1:8来分配,当对象被创建时就会分配到eden区,当eden区容量满了会发生minor GC(Young GC),如若此时存在不能被回收的对象,这些对象被迁移到幸存区1,当再次发生minor GC时,eden区不能被回收的对象会迁移到幸存区2,幸存区1中的对象能被回收的回收掉,不能回收也迁移到幸存区2. 幸存区1和幸存区2保证一个空。当对象进入幸存区后,对象的年龄就开始增加,在1和2之间相互迁移也增加对象的年龄。当对象的年龄超过15(对应的虚拟机参数:-XX:TargetSurvivorRatio),则对象进入到老年代。
在Minor GC过程中,Survivor 可能不足以容纳Eden和另一个Survivor中的存活对象。如果Survivor中的存活对象溢出,多余的对象将被移到老年代,这称为过早提升(Premature Promotion),这会导致老年代中短期存活对象的增长,可能会引发严重的性能问题。再进一步说,在Minor GC过程中,如果老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,这将导致遍历整个Java堆,这称为提升失败(Promotion Failure)。
old代就是老年代,老年代和新生代的空间容量比为2:1。老年代容量占满好会发生Full GC, Full GC会扫描全堆。
持久代在java 8中被移除了,被替换为了元空间。也是上表中的MC。为什么去除持久代
类压缩空间。要理解一个概念,JVM压缩指针。在java当中,参数传递有一个是引用传递,这个引用就是这里所说的指针,普通对象的指针是存储在类压缩空间中。当引用过多的对象时,可能就会发生
java.lang.OutOfMemoryError: Compressed class space 这个时候需要调CompreseedClassSpaceSize(默认大小为1G)
- -class
Loaded | Bytes | Unloaded | Bytes | Time |
---|---|---|---|---|
已加载类的数量 | 已加载类的大小(KB) | 未加载类数量 | 未加载类的大小(KB) | 本次加载耗费的时间(s) |
- -gc
.png)]
S0C | S1C | S0U | S1U | EC | EU | OC | OU | MC | MU | CCSC | CCSU | YGC | YGCT | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
幸存区1容量 | 幸存区2容量 | 幸存区1使用量 | 幸存区2使用量 | eden区容量 | eden使用量 | old代容量 | old待使用量 | 元空间容量 | 元空间使用量 | 类压缩空间容量 | 类压缩空间使用量 | 新生代GC次数 | 新生代GC耗时 | Full GC次数 | Full GC时间 | 总共的GC耗时 |
- -gccapacity
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bTZ6Vgc-1576584409327)(C:\Users\CaoPengfei\AppData\Roaming\Typora\typora-user-images\1576560025481.png)]
NGCMN | NGCMX | NGC | S0C | S1C | EC | OGCMN | OGCMX | OGC | OC | MCMN | MCMX | MC | CCSMN | CCSMX | CCSC | YGC | FGC |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
新生代最小容量 | 新生代最大容量 | 当前新生代容量 | 幸存区1容量 | 幸存区2容量 | Eden容量 | 老年代最小容量 | 老年代最大你容量 | 当前老年代大小 | 老年代容量 | 元数据最小容量 | 元数据最大容量 | 元数据空间容量 | 压缩类空间最小容量 | 压缩类空间最大容量 | 压缩类空间容量 | 年轻代gc次数 | full gc次数 |
- -gcutil
S0 | S1 | E | O | M | CCS | YGC | YGCT | FGC | FGCT | GCT |
---|---|---|---|---|---|---|---|---|---|---|
幸运区1使用占比 | 幸运区2使用占比 | Eden区使用占比 | 老年代使用占比 | 元数据使用占比 | 类压缩空间使用占比 | YOUNG GC次数 | Young GC耗时 | Full GC次数 | Full GC耗时 | 总GC耗时 |
- -gccause
S0 | S1 | E | O | M | CCS | YGC | YGCT | FGC | FGCT | GCT | LGCC | GCC |
---|---|---|---|---|---|---|---|---|---|---|---|---|
幸运区1使用占比 | 幸运区2使用占比 | Eden区使用占比 | 老年代使用占比 | 元数据使用占比 | 类压缩空间使用占比 | YOUNG GC次数 | Young GC耗时 | Full GC次数 | Full GC耗时 | 总GC耗时 | 最近一次GC原因 | GC次数 |
-gcnew -gcnewcapacity -gcold -gcoldcapacity 展示内容以上均有说明在此不再列举展示内容
- -gcnew
- -gcnewcapacity
- -gcold
- -gcoldcapacity
- -compiler
Compiled | Failed | Invalid | time | failedType | FailedMethod |
---|---|---|---|---|---|
编译过类的数量 | 失败数量 | 失效数量 | 耗时 | 失败类型 | 失败方法 |
- -printcompilation
Compiled | Size | Type | Method |
---|---|---|---|
最近编译方法的数量 | 最近编译方法的字节码数量 | 最近编译方法的编译类型 | 方法名标识 |
jinfo
jinfo (Configuration Info for Java)的 作用是实时查看和调整虚拟机各项参数。
- 查看JVM参数
用法 : jinfo -flag
查看元空间最大空间,JVM元空间占用的是物理机内存大小,本机内存16G。
- 调整JVM参数
并不是所有的参数都允许动态修改,使用以下命令可以查看可以修改参数的内容
- windows
java -XX:+PrintFlagsInitial | findstr manageable
- linux
java -XX:+PrintFlagsInitial | grep manageable
本人操作系统为Windows
手动修改CMSAborablePrecleanWaitMillis的值由100 改成了101
如果参数是bool类型,则命令为jinfo -flag +
jmap
jmap (Memory Map for Java) ,用于生成堆转储快照(一般称为dump文件)。如果不使用jmap命令,还要获取dump,也有一些其他手段,比如配置:-XX:+HeapDumpOnOutOfMemoryError。在Linux上使用kill -3命令,发送进程退出信号,虚拟机会生成dump文件。
常用选项
选项 | 作用 |
---|---|
-dump | 生成Java堆转储快照,格式为:-dump:[live,]format=b,file= |
-finalizerinfo | 显示在F-Queue中等待Finalizer线程执行finalize方法的对象。只在Linux/Solaris平台有效 |
-heap | 显示Java堆详细信息,如使用哪种回收器、参数配置、分带状况等。只在Linux/Solaris平台下有效 |
-histo | 显示堆中对象统计信息,包括类、实例数量、合计容量 |
-permstat | 以ClassLoader为统计口径显示永久代内存状态。只在Linux/Solaris平台有效 |
-F | 当虚拟机进程对-dump选项没有响应时,可食用这个选项强制生成dump快照。只在Linux/Solaris平台下有效 |
- -dump
dump在对应目录下已生成。dump文件生成之后需要分析内容,java提供了在线分析工具jhat,但是不太好用。一般在服务器上面生成dump文件后,在本地分析。jdk本身提供了分析工具。在JAVA_HOME/bin/jvisualvm。双击进入,装载生成的dump文件就可以查看堆信息了。下图为分析刚刚生成的Idea.bin dump文件
- -histo
num | instances | bytes | class name |
---|---|---|---|
数量 | 实例数量 | 合计容量 | 类名称 |
jhat
用于分析dump文件,不推荐使用。可以使用更专业的visualVM去分析。在这里就不举例如何使用。
jstack
jstack(Stack Trance for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为thread dump 或者java core文件)。线程快照的目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待。
常用选项
选项 | 作用 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-l | 除堆栈外,显示关于锁的附加信息 |
-m | 如果调用到本地方法的话,可以显示C/C++的堆栈 |
/*死循环代码*/
public static void main(String[] args) {
createBusyThread();
}
/**
* 死循环线程
*/
public static void createBusyThread(){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
}
}
},"testBusyThread");
thread.start();
}
注意打印内容,提示了类中的20行存在死循环。也就是代码中的while(true)的位置。