JVM分析利器

​ 大家在学习JVM的时候知道有好多参数,另外在我们系统在出现问题的时候我们可以接触对JVM中堆使用情况和垃圾回收情况进行分析,同时在jvm调优的时候通过分析GC日志中吞吐量和停顿时间来不断优化,那么对上述指标分析我们除了有JVM的系统理论知识支撑外,还要借助比较优秀的分析利器帮助我们去分析。

JVM分析工具

1.jvm参数

1.1 -XX参数

1
2
3
4
5
6
7
a.Boolean类型
格式:-XX:[+-]<name> +或-表示启用或者禁用name属性
比如:-XX:+UseConcMarkSweepGC 表示启用CMS类型的垃圾回收器
-XX:+UseG1GC 表示启用G1类型的垃圾回收器
b.非Boolean类型
格式:-XX<name>=<value>表示name属性的值是value
比如:-XX:MaxGCPauseMillis=500

1.2 其他参数

1
2
3
-Xms1000等价于-XX:InitialHeapSize=1000
-Xmx1000等价于-XX:MaxHeapSize=1000
-Xss100等价于-XX:ThreadStackSize=100

1.3 设置参数方式

1
2
3
4
开发工具中设置比如IDEA,eclipse
运行jar包的时候:java -XX:+UseG1GC xxx.jar
web容器比如tomcat,可以在脚本中的进行设置
通过jinfo实时调整某个java进程的参数(参数只有被标记为manageable的flags可以被实时修改)

例子

1
2
3
4
5
6
(1)设置堆内存大小和参数打印
-Xmx100M -Xms100M -XX:+PrintFlagsFinal
(2)查询+PrintFlagsFinal的值
:=true
(3)查询堆内存大小MaxHeapSize
:= 104857600

1.4常用参数汇总

参数 含义 说明
-XX:CICompilerCount=3 最大并行编译数 如果设置大于1,虽然编译速度会提高,但是同样影响系 统稳定性,会增加JVM崩溃的可能
-XX:InitialHeapSize=100M 初始化堆大小 简写-Xms100M
-XX:MaxHeapSize=100M 最大堆大小 简写-Xmx100M
-XX:NewSize=20M 设置年轻代的大小
-XX:MaxNewSize=50M 年轻代最大大小
-XX:OldSize=50M 设置老年代大小
-XX:MetaspaceSize=50M 设置方法区大小
-XX:MaxMetaspaceSize=50M 方法区最大大小
-XX:+UseParallelGC 使用UseParallelGC 新生代,吞吐量优先
-XX:+UseParallelOldGC 使用UseParallelOldGC 老年代,吞吐量优先
-XX:+UseConcMarkSweepGC 使用CMS 老年代,停顿时间优先
-XX:+UseG1GC 使用G1GC 新生代,老年代,停顿时间优先
-XX:NewRatio 新老生代的比值 比如-XX:Ratio=4,则表示新生代:老年代=1:4,也就是新 生代占整个堆内存的1/5
-XX:SurvivorRatio 两个S区和Eden区的比值 比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8, 也就是一个S占整个新生代的1/10
-XX:+HeapDumpOnOutOfMemoryError 启动堆内存溢出打印 当JVM堆内存发生溢出时,也就是OOM,自动生成dump 文件
-XX:HeapDumpPath=heap.hprof 指定堆内存溢出打印目录 表示在当前目录生成一个heap.hprof文件
XX:+PrintGCDetails - XX:+PrintGCTimeStamps - XX:+PrintGCDateStamps Xloggc:$CATALINA_HOME/logs/gc.log 打印出GC日志 可以使用不同的垃圾收集器,对比查看GC情况
-Xss128k 设置每个线程的堆栈大小 经验值是3000-5000最佳
-XX:MaxTenuringThreshold=6 提升年老代的最大临界值 默认值为 15
-XX:InitiatingHeapOccupancyPercent 启动并发GC周期时堆内存使用占比 G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆 的使用率,而不只是某一代内存的使用比. 值为 0 则表 示”一直执行GC循环”. 默认值为 45.
-XX:G1HeapWastePercent 允许的浪费堆空间的占比 默认是10%,如果并发标记可回收的空间小于10%,则不 会触发MixedGC。
-XX:MaxGCPauseMillis=200ms G1最大停顿时间 暂停时间不能太小,太小的话就会导致出现G1跟不上垃 圾产生的速度。最终退化成Full GC。所以对这个参数的 调优是一个持续的过程,逐步调整到最佳状态。
-XX:ConcGCThreads=n 并发垃圾收集器使用的线程数量 默认值随JVM运行的平台不同而不同
-XX:G1MixedGCLiveThresholdPercent=65 混合垃圾回收周期中要包括的旧区域设置 占用率阈值 默认占用率为 65%
-XX:G1MixedGCCountTarget=8 设置标记周期完成后,对存活数据上限为 G1MixedGCLIveThresholdPercent 的旧 区域执行混合垃圾回收的目标次数 默认8次混合垃圾回收,混合回收的目标是要控制在此目 标次数以内
- XX:G1OldCSetRegionThresholdPercent=1 描述Mixed GC时,Old Region被加入到 CSet中 默认情况下,G1只把10%的Old Region加入到CSet中

其他常用的参数:

-XX:+CMSParallelInitialMarkEnabled表示在初始标记的多线程执行,减少STW;

-XX:+CMSScavengeBeforeRemark:在重新标记之前执行minorGC减少重新标记时间;

-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,降低STW;

-XX:CMSInitiatingOccupancyFraction=92和-XX:+UseCMSInitiatingOccupancyOnly配套使用,如果不设置后者,jvm第一次会采用92%但是后续jvm会根据运行时采集的数据来进行GC周期,如果设置后者则jvm每次都会在92%的时候进行gc;

-XX:+PrintHeapAtGC:在每次GC前都要GC堆的概况输出

2.常用命令

2.1jps

查看java进程

2.2 info

实时查看jvm参数

1
2
3
4
jinfo -flag name PID   查看某个java进程的name属性的值

jinfo -flag MaxHeapSize PID
jinfo -flag UseG1GC PID

实时修改参数

参数只有被标记为manageable的flags可以被实时修改

1
2
jinfo -flag [+|-] PID
jinfo -flag = PID

2.3 jstat

查看虚拟机性能的统计信息「使用jstat摸清线上系统的JVM运行状况」

1
2
3
4
5
6
7
8
9
10
11
查看类装载信息
jstat -class PID 1000 10
查看垃圾收集信息
jstat -gc PID 1000 10

jstat -gccapacity PID:堆内存分析
jstat -gcnew PID:年轻代GC分析,这里的TT和MTT可以看到对象在年轻代存活的年龄和存活的最大年龄
jstat -gcnewcapacity PID:年轻代内存分析
jstat -gcold PID:老年代GC分析
jstat -gcoldcapacity PID:老年代内存分析
jstat -gcmetacapacity PID:元数据区内存分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
jstat -gc PID
S0C:这是From Survivor区的大小
S1C:这是To Survivor区的大小
S0U:这是From Survivor区当前使用的内存大小
S1U:这是To Survivor区当前使用的内存大小
EC:这是Eden区的大小
EU:这是Eden区当前使用的内存大小
OC:这是老年代的大小
OU:这是老年代当前使用的内存大小
MC:这是方法区(永久代、元数据区)的大小
MU:这是方法区(永久代、元数据区)的当前使用的内存大小
YGC:这是系统运行迄今为止的Young GC次数
YGCT:这是Young GC的耗时
FGC:这是系统运行迄今为止的Full GC次数
FGCT:这是Full GC的耗时
GCT:这是所有GC的总耗时

2.4 jstack

jstack能生成JVM当前时刻的线程快照,以此来定位线程出现长时间停顿的原因,最常见应用场景为:

  • 查看线程间死锁
  • 分析CPU过高原因

查看线程堆栈信息命令

1
jstack [-option]  <pid>  
指令 说明
-F 当jstack指令无响应时,强制打印一个堆栈信息
-m 打印包含Java和C/C++帧的混合模式堆栈跟踪
-l 打印关于锁的其他信息,比如拥有java.util.concurrent ownable同步器的列表
-h / -help 打印帮助信息

cpu 100%了定位如下方法:

1
2
3
4
5
6
7
8
9
10
top -c .    输入P按照cpu使用率排序

top -Hp 进程id 输入P

jstack -l [PID] >/tmp/log.txt

printf "%x\n" 线程pid --> 把线程pid转换成16进制

jstack 进程pid | grep ‘16进制的线程pid’ -C5 --color -->打印进程的堆栈信息
或者 jstack <pid>|grep -A 10 <pid_16>

2.5 jmap

查看打印及dump出堆内存转储快照信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
项目启动参数设置
-XX:+PrintFlagsFinal -Xms300M -Xms300M
打印查看堆内存信息
jmap -heap PID

jmap -histo PID

dump出堆内存相关信息
手动生成dump文件
jmap -dump:format=b,file=heap.hprof PID
然后借助jhat工具查看:启动jhat加载堆转储文件,然后浏览器中访问
jhat dump.hprof -port 7000

项目启动时候参数设置发生内存溢出时候能够自动dump出该文件
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hrprof
注:模式堆内存溢出设置堆内存大小 -Xms20M -Xmx20M

关于dump出的文件怎们去分析,可以借助工具去分析如MAT,perfma

3.常用工具

3.1 jconsole

JDK自带的可视化监控工具

可以查看java应用程序运行概况,监控堆信息,永久区的使用

3.2 jvisualvm

可以i监控本地和远端java进程的cpu,类线程等

远程连接服务器监控远端java进程,需要在远端程序上配置

加入要分析远程java进程,以阿里云服务器tomcat为例子

1.需要修改tomcat中 bin/catalina.sh 这个文件

1
2
3
4
5
6
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -
Djava.rmi.server.hostname=31.100.39.63 -Dcom.sun.management.jmxremote.port=8998
-Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=true -
Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -
Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password"

2.在 ../conf 文件中添加两个文件jmxremote.access和jmxremote.password

jmxremote.access 文件

1
2
guest readonly
manager readwrite

jmxremote.password 文件

1
2
guest guest
manager manager
1
授予权限 : chmod 600 *jmxremot*

3.将连接服务器地址改为公网ip地址

1
2
3
4
hostname -i 查看输出情况
172.26.225.240 172.17.0.1
vim /etc/hosts
172.26.255.240 31.100.39.63

4.启动tomcat

5.查看tomcat启动日志及端口监听

1
2
tail -f ../logs/catalina.out
lsof -i tcp:8080

6.查看8998监听情况,可以发现多开了几个端口

1
2
lsof -i:8998 得到PID
netstat -antup | grep PID

3.3jmc工具

3.4 阿里arthas

github :https://github.com/alibaba/arthas

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器

1
2
3
4
5
6
下载安装:
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
# 然后可以选择一个Java进程
print usage
java -jar arthas-boot.jar -h

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version:查看arthas版本号
help:查看命名帮助信息
cls:清空屏幕
session:查看当前会话信息
quit:退出arthas客户端
---
dashboard:当前进程的实时数据面板
thread:当前JVM的线程堆栈信息
jvm:查看当前JVM的信息
sysprop:查看JVM的系统属性
---
sc:查看JVM已经加载的类信息
dump:dump已经加载类的byte code到特定目录
jad:反编译指定已加载类的源码
---
monitor:方法执行监控
watch:方法执行数据观测
trace:方法内部调用路径,并输出方法路径上的每个节点上耗时
stack:输出当前方法被调用的调用路径

4.分析堆内存

4.1MAT工具

Java堆分析器,用于查找内存泄漏
Heap Dump,称为堆转储文件,是Java进程在某个时间内的快照
下载地址 :https://www.eclipse.org/mat/downloads.php

获取dump文件

1
2
3
4
手动dump
jmap -dump:format=b,file=heap.hprof PID
自动dump
--xx:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=heap.hprof

4.2 perfma在线工具

5 GC日志分析工具

获取GC日志

1
2
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
-Xloggc:gc.log

5.1 在线

1
http://gceasy.io

5.2 GCViewer

分析GC日志的工具:

1.GCHisto http://java.net/projects/gchisto

直接点击gchisto.jar就可以运行,点add载入gc.log

统计了总共gc次数,youngGC次数,FullGC次数,次数的百分比,GC消耗的时间,百分比,平均消耗时间,消耗时间最小最大值等

2.GCLogViewer http://code.google.com/p/gclogviewer/

点击run.bat运行

整个过程gc情况的趋势图,还显示了gc类型,吞吐量,平均gc频率,内存变化趋势等

Tools里还能比较不同gc日志

6 jvm参数配置模版

如何在JVM内存溢出OOM时候自动dump内存快照?

设置JVM参数:

-XX:+HeapDumpOnOutOfMemoryError

-XX:+HeapDumpPath=/usr/local/app/oom

jvm参数中加入打印GC日志

-XX:+PrintGCDetails 打印详细的GC日志

-XX:+PrintGCTimeStamps 打印出来每次GC发生的事件

-Xloggc:gc.log 这个参数可以设置将gc日志写入一个磁盘文件

下面总结一下JVM参数模板:

1
-Xms4096M -Xmx4096M -Xmn3072M -Xss1M  -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/app/oom