个人技术分享

jvm性能优化工具

jdk提供给我们了很实用的工具来分析JVM的状态,线程以及配置,这些工具包含于jdk中,并且以java实现,是JVM性能优化必不可少的工具集,这些工具都在$JAVA_HOME/bin下

jps、jinfo、jstack、jmap、jstat基本使用

先说下各个命令的作用

  • jps : 查看虚拟机进程工具
  • jinfo:查看虚拟机配置工具,需要进程id
  • jstack:虚拟机线程快照工具 ,可以用来排查死锁,或线程长时间停滞的问题
  • jmap:虚拟机堆快照工具,生成dump文件,用来与MAT工具混合使用
  • jstat :收集虚拟机各方面运行数据

jps(jvm process status tool)

jps [options] [hostid]
配置 作用
-q 忽略主类的名称,只输出pid
-m 输出启动类main函数的参数
-l 输出主类名,如果进程执行的为jar,则输出jar路径
-v 输出具体进程启动时jvm参数

如不指定hostid,则默认当前主机,常用方式 jps -ml | jps -lv

jinfo ( configuration info for java ) 

  • jinfo pid : 显示jvm系统属性与vm参数信息
  • jinfo -flags pid : 显示jvm vm参数信息,如最大最小堆,默认堆,垃圾收集器参数
  • jinfo -sysprops pid : 显示jvm系统属性
  • jinfo -flag xxx pid : 显示特定vm参数值 jinfo -flag MaxHeapSize pid 输出pid的最大堆内存

jstack ( stack trace for java )

jstack [options]  pid
配置项 作用

-f

当正常输出请求不被响应时,强制输出线程栈堆,当Java进程负载较高的时候,可以加上该参数,强制dump线程快照
-l 除线程栈堆外,显示关于锁的附加信息
-m 如果调用本地方法的话,可以显示c/c++的栈堆

jmap(memory map for java )

jmap [option] pid
配置项 作用
-heap  查看当前jvm heapdump与垃圾收集器的使用情况

-dump:format=b,file=filename.hprof pid

转储堆快照,生成hprof文件到指定路径
-histo pid  列出当前heap中对象状况,附字节码与java对象映射表


Heap Configuration:                                    #堆配置情况,也就是JVM参数配置的结果
   MinHeapFreeRatio         = 0                        #最小堆使用比例
   MaxHeapFreeRatio         = 100                      #最大堆可用比例
   MaxHeapSize              = 8522825728 (8128.0MB)    #最大堆空间大小
   NewSize                  = 177209344 (169.0MB)      #新生代分配大小
   MaxNewSize               = 2840592384 (2709.0MB)    #最大可新生代分配大小
   OldSize                  = 355467264 (339.0MB)      #老年代大小
   NewRatio                 = 2                        #新生代比例
   SurvivorRatio            = 8                        #新生代与suvivor的比例
   MetaspaceSize            = 21807104 (20.796875MB)   #元空间大小
   CompressedClassSpaceSize = 1073741824 (1024.0MB)    #压缩类空间大小
   MaxMetaspaceSize         = 17592186044415 MB        #最大元空间大小
   G1HeapRegionSize         = 0 (0.0MB)                #G1的region大小

Heap Usage:                                            #堆使用情况
PS Young Generation                                    #新生代(Eden区 + survior(from + to)区)
Eden Space:                                            #Eden区
   capacity = 1928855552 (1839.5MB)                    #Eden区容量
   used     = 267720912 (255.3185577392578MB)          #已经使用大小
   free     = 1661134640 (1584.1814422607422MB)        #剩余容量
   13.87978025220211% used                             #使用比例
From Space:                                            #survior0区
   capacity = 66060288 (63.0MB)                        #survior0区容量
   used     = 0 (0.0MB)                                #survior0已经使用大小
   free     = 66060288 (63.0MB)                        #survior0剩余容量
   0.0% used                                           #survior0使用比例
To Space:                                              #survior1区
   capacity = 85983232 (82.0MB)                        #survior1区容量
   used     = 0 (0.0MB)                                #survior1已经使用大小
   free     = 85983232 (82.0MB)                        #survior1剩余容量
   0.0% used                                           #survior1使用比例
PS Old Generation                                      #老年代使用情况
   capacity = 695730176 (663.5MB)                      #老年代容量
   used     = 207137472 (197.54168701171875MB)         #老年代已使用大小
   free     = 488592704 (465.95831298828125MB)         #老年代剩余大小
   29.77267324969386% used                             #老年代使用比例

jmap -histo:live pid  显示堆中对象的统计信息,如果指定了live子选项,则只计算活动的对象

jmap -dump:format=b,file=heapdump.hprof pid,dump当前内存快照,以hprof二进制格式转储Java堆到指定filename的文件中,live子选项是可选的 

可以通过MT工具或者jdk自带的jvisualvm装载dump文件分析问题,

-XX:+HeapDumpOnOutOfMemoryError配置这玩意之后,oom的时候会自动dump的,到时候拿快照分析一波就好了 

jstat ( jvm statistics monitoring tool)

jstat -< option > [-t] [-h] pid [< interval > [< count >]]
  • -t 参数可以在输出信息前面加上一个 Timestamp 列,显示程序运行的时间
  • -h 参数可以周期数据输出时,输出多少行后,跟着输出一个表头信息
  • interval 表示循环时间间隔,默认单位为ms,可以在直接使用s/ms指定单位,如 60ms/1s, count 表示输出几次 ,下面是查询每10s 查询20次gc情况
jstat gc pid 10s 20  
配置项         作用
-class 监视类装载、卸载数量、总空间以及类装载所耗费的时间
-gc 监视Java堆,包括Eden区、两survivor区、老年代、永久代等的容量、已用空间、GC时间合计等
-gccapacity 与-gc基本相同,但关注点为Java堆各个区域使用到的最大、最小空间
-gcutil  与-gc基本相同,但关注点为Java堆各个区域已使用空间占总空间的百分比
-gccause 与-gcutil功能相同,但会额外输出导致上一次GC产生的原因
-gcnew  监控新生代GC情况
-gcnewcapacity 与-gcnew基本相同,但关注最大,最小空间
-gcold     监控老年代GC情况
gcoldcapacity  与-gcold基本相同,但关注最大,最小空间
-compiler  输出被JIT编译过的方法、耗时等信息
-printcomplilation 输出已经被JIT编译的方法

查询gc情况 jstat -gc pid ,属性含义后缀是C代表容量,后缀是U代表已使用,后缀是T代表的是时间(秒)

属性 含义
S0C  新生代survivor0容量
S1C   新生代survivor1容量
S0U 新生代survivor0已使用大小
S1U 新生代survivor1已使用大小
EC  新生代eden区容量
OC  老年代容量
OU 老年代已使用大小
MC 元数据容量即方法区容量
MU 元数据已使用空间
CCSC  压缩类空间大小
CCSU 压缩类空间使用大小
YGC  新生代gc次数(young gc)
YGCT 新生代gc时间(s)
FGC 老生代gc次数(full gc)
GCT 总的gc时间,包括young gc和full gc

 jstat -gccapacity pid 查看各空间容量

属性 含义
NGCMN  新生代最小容量
NGCMX 新生代最大容量
NGC 当前新生代容量
S0C  survivor0大小
S1C survivor1大小
EC  Eden区大小
OGCMN 老年代最小容量
OGCMX 老年代最大容量
OGC 当前老年代大小
MCMN  元数据最小容量
MCMX 元数据最大容量
CCSMN 最小压缩类空间大小
CCSMX 最大压缩类空间大小
CCSC 当前压缩类空间大小

可视化工具jvisualvm和jconsole控制台

这两种方式都能可视化监控java程序的运行状态,不过VisualVm界面更美观,数据更实时,所以推荐使用VisUalVm

 jvisualvm

命令行窗口输入jvisualvm,即可打开visualvm,可以查看本地java进程和远程java进程,支持导入dump文件

jconsole

命令行窗口输入jconsole,打开jconsole工具,可以查看java程序线程,内存、堆等信息

JVM问题排查步骤

1、top命令找到占用线程最高的进程

top

2、进入线程模式查找过高的线程pid

top -Hp  获取占用cpu过高的线程pid(1)中的pid

3、线程pid转换为十六进制

printf "%x\n" 占用最高的线程pid (2)中的pid

4、使用命令jstack pid| grep 0x16进制pid -A30 找到占用cpu资源过高的代码

因为我这是tomcat进程,所以没有定位到代码 ,正常情况会定位到代码栈信息,就可以定位问题啦 

除去上述4个步骤、也可以这样排查:

设置打印gc日志参数-XX:+PrintGCDetails -Xloggc:/data/jvm/gc.log,然后去在线的gceasy网站,上传日志分析结果;官网:https://gceasy.io/