当我们的程序有卡顿,oom,死锁等等各种情况我们如果是自己写的代码,还能大概清楚出问题的代码在哪儿,如果你作为项目经理,具体业务代码并不清楚,要查找这些问题就需要jvm调优工具,当然也可以用以装逼。。。好了,那么我们一起来看下如何使用jvm的这些命令
前置条件,本地开启一个java项目,什么项目都可以
JPSjps命令语法:jps [options ] [ hostid ] 参数可以不写
[options]参数 :
-q:仅输出进程 id,不包括classname,jar name,arguments in main method
-m:输出main method的参数
-l:输出彻底的包名,应用主类名,jar的彻底路径名
-v:输出jvm参数
-V:输出经过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件
-Joption:传递参数到vm,例如:-J-Xms512m
[hostid]:
[protocol:][[//]hostname][:port][/servername]web
协议 主机名 端口 服务名称 (可以远程查看其他服务器)
使用:
1. 打开cmd命令窗口,输入jps 查看当前运行的 java 进程ID 和 启动类的名称
如果输入没有任何反应:(没问题的往下继续)
a.打开文件地址栏转到:%TMP%
b.找到 hsperfdata_user(user是当前电脑登录用户名)文件夹
c. 右键属性》安全》找到对应用户,编辑》完全控制,应用后
d. 再运行java程序,看下里面有没有生成文件,又就可以再jps
2. 其他参数自行测试
ps: 这个参数最常用的作用应该是查询当前java进程id(本人使用如此)
JMAPjmap命令是一个可以输出所有内存中对象的工具,也可以将VM 中的heap,以二进制输出成文本。
命令格式:
jmap [option]
jmap [option]
jmap [option] [server_id@]
(to connect to remote debug server) 连接到远程调试服务
1. -heap 打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况
例: jmap -heap pid (附翻译)
2. -finalizerinfo 打印正等候回收的对象的信息
例: jmap -finalizerinfo pid (附翻译) 现在是0个待回收
3. -dump:[live,]format=b,file=
例:jmap -dump:live,format=b,file=test.txt pid
输出到了当前目录(这个文件打开看不懂,后面我们用jvisualvm去分析就好了)
4. -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量。
例:jmap -histo:live pid (截图部分,太多了,一般不太会用这个查看,不好观察)
JSTAT jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下: jstat [-命令选项] [pid] [间隔时间(毫秒)] [查询次数] 注意:使用的jdk版本是jdk8 命令选项如下: 垃圾回收使用统计 jstat -gc pid 1000指间隔一秒查询一次,后面按语法还可以指定查询次数 S0C:第一个幸存区的大小,单位KB S1C:第二个幸存区的大小 S0U:第一个幸存区的使用大小 S1U:第二个幸存区的使用大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 OC:老年代大小 OU:老年代使用大小 MC:方法区大小(元空间) MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间,单位s FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间,单位s GCT:垃圾回收消耗总时间,单位s 堆内存统计jstat -gccapacity pid
NGCMN:新生代最小容量 NGCMX:新生代最大容量 NGC:当前新生代容量 S0C:第一个幸存区大小 S1C:第二个幸存区的大小 EC:伊甸园区的大小 OGCMN:老年代最小容量 OGCMX:老年代最大容量 OGC:当前老年代大小 OC:当前老年代大小 MCMN:最小元数据容量 MCMX:最大元数据容量 MC:当前元数据空间大小 CCSMN:最小压缩类空间大小 CCSMX:最大压缩类空间大小 CCSC:当前压缩类空间大小 YGC:年轻代gc次数 FGC:老年代GC次数 新生代回收情况jstat -gcnew pid
S0C:第一个幸存区的大小 S1C:第二个幸存区的大小 S0U:第一个幸存区的使用大小 S1U:第二个幸存区的使用大小 TT:对象在新生代存活的次数 MTT:对象在新生代存活的最大次数 DSS:期望的幸存区大小 EC:伊甸园区的大小 EU:伊甸园区的使用大小 YGC:年轻代垃圾回收次数 YGCT:年轻代垃圾回收消耗时间 老年代回收统计jstat -gcold pid
MC:方法区大小 MU:方法区使用大小 CCSC:压缩类空间大小 CCSU:压缩类空间使用大小 OC:老年代大小 OU:老年代使用大小 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间 元空间回收统计jstat -gcmetacapacity pid
MCMN:最小元数据容量 MCMX:最大元数据容量 MC:当前元数据空间大小 CCSMN:最小压缩类空间大小 CCSMX:最大压缩类空间大小 CCSC:当前压缩类空间大小 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间 堆空间百分比统计jstat -gcutil pid
S0:幸存1区当前使用比例 S1:幸存2区当前使用比例 E:伊甸园区使用比例 O:老年代使用比例 M:元数据区使用比例 CCS:压缩使用比例 YGC:年轻代垃圾回收次数 FGC:老年代垃圾回收次数 FGCT:老年代垃圾回收消耗时间 GCT:垃圾回收消耗总时间jstat 是做jvm调优关键的命令,用这个命令我们可以分析出很多的问题:
年轻代对象增长的速率
可以执行命令 jstat -gc pid 1000 10 (每隔1秒执行1次命令,共执行10次),通过观察EU(eden区的使用)来估算每秒eden大概新增多少对象,如果系统负载不高,可以把频率1秒换成1分钟,甚至10分钟来观察整体情况。注意,一般系统可能有高峰期和日常期,所以需要在不同的时间分别估算不同情况下对象增长速率。
Young GC的触发频率和每次耗时 知道年轻代对象增长速率我们就能推根据eden区的大小推算出Young GC大概多久触发一次,Young GC的平均耗时可以通过 YGCT/YGC公式算出,根据结果我们大概就能知道 系统大概多久会因为Young GC的执行而卡顿多久。 每次Young GC后有多少对象存活和进入老年代 这个因为之前已经大概知道Young GC的频率,假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次 Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出 老年代对象增长速率。 Full GC的触发频率和每次耗时 知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。 优化思路 其实简单来说就是尽量让每次Young GC后的存活对象小于Survivor区域的50%,都留存在年轻代里。尽量别让对象进入老年代。尽量减少Full GC的频率,避免频繁Full GC对JVM性能的影响。调优也不是一蹴而就的,不是说你查看了各种参数一顿分析然后调整参数就可以搞定了,这个是循序渐进的,再厉害的人也不可能一上来就能准确的说到问题点,分析只是找方向,经验越多,方向就会更正确,找问题就会更快速,实践才能检测真理
调优工具调优工具就是把上面的命令封装了,然后变成可视化的操作,方便我们使用
jvisualvmjava自带的一个jvm调优工具,使用也很简单,在自己的jdk安装目录里面就能找到
jdk/bin/jvisualvm.exe打开就好
默认监控本地所欲的java运行程序,其实应该就是上面的命令的可视化操作版本,也支持远程
稍微看下:
随便点开一个java程序
当前运行jvm参数,监控cpu,线程等等自行操作
还可以把我们前面导出的test.txt放入查看:
点击文件》装入》记得选择堆 Dump》选择文件打开
可以看到,前面提到的文件就是查看当前的实例一些东西,查大量对象的时候经常使用,比如一个程序一直在创建对象,也没有回收,这个就能够找到是谁
ps: 一般不用 jmap 去查询 ,在监控里面点击 堆Dump
Arthas
阿里旗下的调优工具,没有使用过,据说挺好用,记录一下,以后可能会使用
api: https://alibaba.github.io/arthas
GC日志分析 对于java应用我们可以通过一些配置把程序运行过程中的gc日志全部打印出来,然后分析gc日志得到关键性指标,分析 GC原因,调优JVM参数。 加入参数输出 gc日志( Tomcat则直接加在 JAVA_OPTS变量里)-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:D:gc-%t.log
空的spring boot项目,没啥特别多的内容稍微分析下一些东西
上面的是参数,没啥特别的,基本都默认
1. 对于 1.349: 这是从jvm启动开始计算到这次GC经过的时间,还有具体的发生时间日期2. Full GC 是一次full gc,括号里是gc的原因, PSYoungGen是年轻代的GC,ParOldGen是老年代的GC,metaspace是元空间的GC(本次Full GC 原因就是元空间小了,默认21M不够)
3. 10226K -> 0K(73728K) 这三个数字分别对应GC之前分代占用的大小,GC之后分代占用,以及整个分代的大小。 young 年轻代 old 老年代 metaspace元空间 4. 0.0212227本次gc耗时 分析处理: 可以看到元空间回收前大概21M ,回收了个寂寞,都没有内容被回收,所以jvm自动调大元空间, old区的值如何解释呢? 1477K ->11146k(111104K) 11704K->11146k(184832K) 也即GC前old区占用空间是1477K,GC后占用空间11146K,总空间111104K( 为啥回收后反而更大了?启动的时候,需要把很多spring boot 的内容存放到老年代,gc触发回收前没有多少内容,但是又不断的往老年代里面放,导致更多的数据进入,原来进入的数据其实大部分都没办法回收,因为spring boot 需要这些对象存活) 堆GC前占用空间11704K,GC后占用空间11146K,总空间184832K 设置元空间大一点 -XX:metaspaceSize=200M :现在就没有Full GC 了其他原因导致的Full GC就对应处理



