jdk自带的jvm监控工具

Java自带了几个jvm监控工具,如jstat、jmap、jstack。

jstat

jstat是常见的线上jvm问题排查工具,jstat用法:

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM


说明:

lines: 使用interval参数,会在间隔指定时间后输出当前JVM内存的状态,这个参数是指定输出多少行后,再输出title,这样就不需要翻屏看这一列的title了。
vmid: 虚拟机的pid
interval:间隔多少时间后循环输出,不指定的话,就输出一次
count:指定输出多少次
jstat -option选项

]# jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation

jstat -gc

间隔1s统计输出一次,共统计10次。

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_02


含义详解:

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_03


Java 使用VisualVM排查未关闭流 jvm排查工具_sed_04


jstat -gcutil

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_05


每隔lines显示title: jstat -gcutil -h pid interval

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_06


详细含义:

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_07


jstat -gccapacity

Java 使用VisualVM排查未关闭流 jvm排查工具_sed_08


Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_09


jmap

用法:

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_10


-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.

-h | -help 打印辅助信息

-J 传递参数给jmap启动的jvm.

jmap -heap pid

打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.

Concurrent Mark-Sweep GC

using parallel threads in the new generation. //新生代采用的是并行线程处理方式
using thread-local object allocation.
Concurrent Mark-Sweep GC //同步并行垃圾回收

Heap Configuration: //堆配置情况
MinHeapFreeRatio = 40 //最小堆使用比例
MaxHeapFreeRatio = 70 //最大堆可用比例
MaxHeapSize = 2147483648 (2048.0MB) //最大堆空间大小
NewSize = 268435456 (256.0MB) //新生代分配大小
MaxNewSize = 268435456 (256.0MB) //最大可新生代分配大小
OldSize = 5439488 (5.1875MB) //老生代大小
NewRatio = 2 //新生代比例
SurvivorRatio = 8 //新生代与suvivor的比例
PermSize = 134217728 (128.0MB) //perm区大小
MaxPermSize = 134217728 (128.0MB) //最大可分配perm区大小

Heap Usage: //堆使用情况
New Generation (Eden + 1 Survivor Space): //新生代(伊甸区 + survior空间)
capacity = 241631232 (230.4375MB) //伊甸区容量
used = 77776272 (74.17323303222656MB) //已经使用大小
free = 163854960 (156.26426696777344MB) //剩余容量
32.188004570534986% used //使用比例
Eden Space: //伊甸区
capacity = 214827008 (204.875MB) //伊甸区容量
used = 74442288 (70.99369812011719MB) //伊甸区使用
free = 140384720 (133.8813018798828MB) //伊甸区当前剩余容量
34.65220164496263% used //伊甸区使用情况
From Space: //survior1区
capacity = 26804224 (25.5625MB) //survior1区容量
used = 3333984 (3.179534912109375MB) //surviror1区已使用情况
free = 23470240 (22.382965087890625MB) //surviror1区剩余容量
12.43827838477995% used //survior1区使用比例
To Space: //survior2 区
capacity = 26804224 (25.5625MB) //survior2区容量
used = 0 (0.0MB) //survior2区已使用情况
free = 26804224 (25.5625MB) //survior2区剩余容量
0.0% used // survior2区使用比例
concurrent mark-sweep generation: //老生代使用情况
capacity = 1879048192 (1792.0MB) //老生代容量
used = 30847928 (29.41887664794922MB) //老生代已使用容量
free = 1848200264 (1762.5811233520508MB) //老生代剩余容量
1.6416783843721663% used //老生代使用比例
Perm Generation: //perm区使用情况
capacity = 134217728 (128.0MB) //perm区容量
used = 47303016 (45.111671447753906MB) //perm区已使用容量
free = 86914712 (82.8883285522461MB) //perm区剩余容量
35.24349331855774% used //perm区使用比例

Parallel GC

using thread-local object allocation.
Parallel GC with 4 thread(s) //GC 方式

Heap Configuration: //堆内存初始化配置
MinHeapFreeRatio=40 //对应jvm启动参数-XX:MinHeapFreeRatio设置JVM堆最小空闲比率(default 40)
MaxHeapFreeRatio=70 //对应jvm启动参数 -XX:MaxHeapFreeRatio设置JVM堆最大空闲比率(default 70)
MaxHeapSize=512.0MB //对应jvm启动参数-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1.0MB //对应jvm启动参数-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize =4095MB //对应jvm启动参数-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 4.0MB //对应jvm启动参数-XX:OldSize=:设置JVM堆的‘老生代’的大小
NewRatio = 8 //对应jvm启动参数-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //对应jvm启动参数-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize= 16.0MB //对应jvm启动参数-XX:PermSize=:设置JVM堆的‘永生代’的初始大小
MaxPermSize=64.0MB //对应jvm启动参数-XX:MaxPermSize=:设置JVM堆的‘永生代’的最大大小

Heap Usage: //堆内存分步
PS Young Generation
Eden Space: //Eden区内存分布
capacity = 20381696 (19.4375MB) //Eden区总容量
used = 20370032 (19.426376342773438MB) //Eden区已使用
free = 11664 (0.0111236572265625MB) //Eden区剩余容量
99.94277218147106% used //Eden区使用比率
From Space: //其中一个Survivor区的内存分布
capacity = 8519680 (8.125MB)
used = 32768 (0.03125MB)
free = 8486912 (8.09375MB)
0.38461538461538464% used
To Space: //另一个Survivor区的内存分布
capacity = 9306112 (8.875MB)
used = 0 (0.0MB)
free = 9306112 (8.875MB)
0.0% used
PS Old Generation //当前的Old区内存分布
capacity = 366280704 (349.3125MB)
used = 322179848 (307.25464630126953MB)
free = 44100856 (42.05785369873047MB)
87.95982001825573% used
PS Perm Generation //当前的 “永生代” 内存分布
capacity = 32243712 (30.75MB)
used = 28918584 (27.57891082763672MB)
free = 3325128 (3.1710891723632812MB)
89.68751488662348% used

生成jvm dump快照

jmap -dump:format=b,file=/home/work/jinze/dump_26041_05231438.hprof ${pid} --> 生成二进制堆栈文件。
jmap -heap ${pid} --查看jvm堆栈内存占用情况
jmap -histo:live ${pid} | head -20  --查看jvm内存占用前20存活对象

map–histo

-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量。

]$ jmap -histo:live 14645 | head -10

num     #instances         #bytes  class name
----------------------------------------------
   1:        901564     1777759440  [B
   2:        112206      123401632  [J
   3:       1462437       92280272  [C
   4:       2182305       69833760  java.util.concurrent.atomic.LongAdder
   5:        622976       68936400  [Ljava.lang.Object;
   6:       1518179       48581728  java.util.HashMap$Node
   7:        936736       44963328  java.util.HashMap
   8:        960914       38436560  java.util.TreeMap$Entry
   9:       1453265       34878360  java.lang.String
  10:       1380194       33124656  org.elasticsearch.common.util.concurrent.ReleasableLock

jmap -permstat

-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.

jstack
jstack是jdk中自带的用于查看进程内线程栈的工具,很轻量易用,并且执行时不会对性能造成很大的影响。当程序出现死锁时,我们可以通过jstack打印线程栈找到问题。

jstack用法:

Java 使用VisualVM排查未关闭流 jvm排查工具_ci_11


使用jstack定位问题

找出CPU消耗多的代码

如果程序cpu占用很高,我们需要找到问题并优化,可以配合top命令,找出最耗cpu的进程,从而找到相应代码解决问题。

1、先用jps找出程序pid,这里是23034

2、用top命令找出该进程最耗cpu的线程。

~]# top -Hp 23034

结果如下图

Java 使用VisualVM排查未关闭流 jvm排查工具_sed_12


23046线程占了93.8的cpu,就是它。

3、将23046转成16进制。因为top里的pid是10进制,而jstack里是16进制,叫nid。

可以用printf命令转换:

~]# printf “%x\n” 23046

得到 5a06

4、jstack出场

~]# jstack 23034 | grep 5a06

结果如下:

Java 使用VisualVM排查未关闭流 jvm排查工具_JVM_13


writeFileThread占用了最多的cpu资源。找到后,可以优化代码了。

使用jstack时,可能遇到下面的情况:

Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding

原因:
1、jstack报错:Unable to open socket file。是因为这个java进程的pid文件删除了。
2、从Java 6 update 21 引入的bug sunbug 7009828,在Java 6 update 25修复。

解决办法:
1 修改tmpwatch设置

排查对应的/tmp/hsperfdata_的目录,让jvm自己来管理,保证jps,jstat等命令可用。
修改/etc/cron.daily/tmpwatch
/usr/sbin/tmpwatch “$flags” -x /tmp/hsperfdata_
-x /tmp/.X11-unix -x /tmp/.XIM-unix
-x /tmp/.font-unix -x /tmp/.ICE-unix -x /tmp/.Test-unix 240 /tmp
2 java程序重启
3 由于jdk版本导致的问题,需要升级jdk版本

Thread Dump收集

stack -l  <pid> > <file-path>

For Example:l

jstack -l 37320 > /opt/tmp/threadDump.txt

堆分析工具
Memory Analyzer(MAT)

是一款Java堆分析工具,能够快速找到占用堆内存空间最多的对象,以便程序进行优化,减少内存消耗,定位可能的内存泄漏问题。

应用程序分析
Visual VM的Profiler

可以查看到程序对CPU和内存的使用情况,如分析程序中执行次数最多最耗时的方法,占用内存空间最多的对象等等。

程序动态跟踪工具 BTrace
BTrace是一个开源的Java程序动态跟踪工具。它通过Hotspot虚拟机的HotSwap技术将跟踪的代码动态替换到被跟踪的Java程序内,以观察程序运行的细节。

这个功能在实际的生产环境中十分有意义,每当在线运行的系统出现问题时,通过使用BTrace,可以在不修改代码、不重启应用的情况下,动态的查看程序运行的细节,方便的对程序进行调试。