1、学习JDK自带的几款在线监控命令(JPS、jstat、jstack、jmap)。
2、JVM离线分析工具(Visualvm)的使用。
3、第三方在线监控工具(Arthas)的使用。
1、在线监控工具
1.1、JPS (打印Java进程信息)
使用场景 : 查看当前机器的所有Java进程信息(可追踪到应用进程ID 、启动类名、文件路径。)。
指令格式 : jps 【选项 】 [hostid]
[hostid] : 远程地址,可选参数,指定特定主机的IP或者域名,也可以指定具体协议端口,不指定则查看当前机器的相关信息,hostid所指机器必须开启jstatd服务。

常用命令: jps -l
查看当前虚拟器运行的进程,输出主类全路径名称。

1.2、Jstat (JVM内存信息统计)
使用场景 : 用于查看各个功能和区域的统计信息(如:类加载、编译相关信息统计,各个内存区域GC概况和统计)
格式 : jstat 【选项】 【进程ID】 [间隔时间 ] [查询次数]
[间隔时间 ] 可选参数,每隔多长时间输出一次信息,单位为毫秒。
[查询次数] 可选参数,总共输出多少次信息。

常用指令:jstat -gc PID
查看即时内存使用情况、垃圾回收统计信息,用于分析GC情况。
arduino
指令: jstat -gc 16462 1000 5
//查看16462进程 应用的堆内存使用、垃圾回收统计信息,每隔开1000毫秒输出一次,总共输入5次 。

参数说明:
arduino
S0C 和 S0U //S0区的总内存大小和已使用的内存大小。
S1C: 和S1U //S1区的总内存大小和已使用的内存大小。。
EC 和 EU //Eden区的总内存大小 和已使用的内存大小。
OC和OU //Old区的总内存大小 和已使用的内存大小。
MC和MU //方法区的总内存大小 和已使用的内存大小。
CCSC和CCSU //压缩类空间大小 和已使用的内存大小。
YGC和 YGCT //Young GC 的总次数 和消耗总时间。
FGC和 FGCT //Full Gc的总次数和消耗总时间。
GCT //所有GC的消耗时间。
1.3、Jinfo(JVM参数查看和修改)
使用场景: 查看和调整JVM启动和运行参数。
格式:
jinfo 【进程ID】
jinfo 【选项】【进程ID】
jinfo 【选项】【具体选项参数名】【进程ID】
案例一:查看JVM整个系统参数信息
比如:输出16462进程jvm的全部参数和系统属性。
指令 : jinfo 16462

案列二: 查看某个具体参数
比如:查看老年代内存大小
指令:jinfo -flag OldSize 16462

案列三:启用某个配置
比如:开启堆内存溢出日志打印(默认是关闭的)。
指令:jinfo -flag +PrintGCDetails 16462

案例四:修改某个参数值
比如:修改当堆内存对象所占空间超过80%时进行扩容
指令:jinfo -flag MaxHeapFreeRatio=80 16462

案列五:启用某个配置
比如: 启动GC日志打印
指令:jinfo -flag +PrintGCDetails 16462

1.4、Jmap(JVM内存占用信息和快照)
使用场景: 监控堆内存使用情况和对象占用情况, 生成堆内存快照文件,查看堆内存区域配置信息。
格式: jmap 【选项】【进程ID】

案例一:查看堆内存的配置和使用情况
命令: jmap -heap 18230
ini
Heap Configuration:
MinHeapFreeRatio = 0 //JVM堆缩减空间比率,低于则进行内存缩减
MaxHeapFreeRatio = 100 //JVM堆扩大内存空闲比例,高于则进行内存扩张
MaxHeapSize = 994050048 (948.0MB) //堆最大内
NewSize = 20971520 (20.0MB) //新生代初始化内存大小
MaxNewSize = 331350016 (316.0MB) //新生代最大内存大小
OldSize = 41943040 (40.0MB) //老年代内存大小
NewRatio = 2 //新生代和老年代占堆内存比率
SurvivorRatio = 8 //s区和Eden区占新生代内存比率
MetaspaceSize = 21807104 (20.796875MB) //元数据初始化空间大小
CompressedClassSpaceSize = 1073741824 (1024.0MB) //类指针压缩空间大小
MaxMetaspaceSize = 17592186044415 MB //元数据最大内存代销
G1HeapRegionSize = 0 (0.0MB) //G1收集器Region单元大小
Heap Usage:
PS Young Generation
Eden Space:
capacity = 303038464 (289.0MB) //Eden区总容量
used = 22801000 (21.744728088378906MB) //Eden区已使用荣浪
free = 280237464 (267.2552719116211MB) //Eden区剩余容量
7.524127366221075% used //Eden区使用比例
From Space: //From区(也就是Survivor中的S1区)
capacity = 13107200 (12.5MB) //S1区总容量大小
used = 5364536 (5.116020202636719MB) //S1区已使用大小
free = 7742664 (7.383979797363281MB) //S1区剩余大小
40.92816162109375% used //S1使用比例
To Space: //To区 (也就是Survivor中的S2区)
capacity = 13631488 (13.0MB) //S2区总容量大小
used = 0 (0.0MB) //S2区已使用大小
free = 13631488 (13.0MB) //S2区剩余大小
0.0% used //S2区使用比率
PS Old Generation
capacity = 110624768 (105.5MB) //老年代总容量大小
used = 49431224 (47.14128875732422MB) //老年代已使用大小
free = 61193544 (58.35871124267578MB) //老年代剩余大小
44.68368602589973% used //老年代使用功能比例
案例二:查看JVM中对应类型对象的数量、占用内存情况
命令:
bash
jmap -histo 18230 | sort -n -r -k 2 | head -10 //统计实例最多的类 前十位有哪些
jmap -histo 18230 | sort -n -r -k 3 | head -10 //统计合计容量前十的类有哪些

案例三:dump 堆快照
命令:jmap -dump:live,format=b,file=/home/myheapdump.hprof 18230
lua
live 加上live代表只dump存活的对象 ;
fomat 格式
filie 导出的文件名
18230 java进程ID
这里生成的 dump文件可以用我们后面讲的可视化工具VisualVM来打开文件对里面的内容进行分析。
1.5、jstack(JVM线程信息监控)
使用场景: 查看JVM线程信息 和生成线程快照。
格式:
jstack 【选项】 【进程ID】
jstack [ 选项 ] executable core
jstack [ 选项 ] [进程ID]远程IP

案例一:打印堆栈线程信息 ,输出到文件
jstack -l 871055 >jstack.prof

2、离线分析工具(VisualVM)
2.1、VisualVM安装
VisualVM 不需要额外安装,我们安装JDK的时候就自带了VisualVM,在安装JDK的 bin目录下可以找到jvisualvm.exe
3、第三方在线监控工具(Arthas)
Arthas
是Alibaba开源的Java诊断工具,它可以帮助我们解决在线环境的以下问题
-
监控到JVM的实时运行状态(涵盖Jps ,jstat ,jinfo ,jstack ,部分Jmap功能)。
-
在不需要重新部署服务的情况下修改业务代码。
-
以局视角来查看系统的运行状况。
3-1、Arthas 安装
(1)下载`
arduino
curl -O https://arthas.aliyun.com/arthas-boot.jar
(2)启动
java -jar arthas-boot.jar
(3)选择监控的应用
athas启动成功后会打印出来当前服务器的所有java进程,然后以编号的形式排列出来,在控制台输入你想要监控的应用编号即可进入arthas的监控控制台,这里我选择了编号为7的应用进行监控。

3-2、常用命令
(1)、查看系统实时运行状态(dashboard)
在控制台输入dashboard 回车后会看到下图对应的信息,这里主要展示了当前监控的进程信息、包括 实时的线程信息、内存分配和使用状态信息、系统环境信息。

(2) 、查看应用线程信息(thread)
thead 可现实应用的运行线程情况,功能和jstack类似。
在控制台输入thread 可显示当前应用运行的线程信息。
arduino
thread

在控制台输入tread ID 可查看某一个线程的信息。
tread 14

(3) 、修改应用中的某个类代码并动态编译和加载类到JVM
如果需要修改正在运行中的应用程序代码就很可能需要下面几个指令来帮助你完成此动作。
第一步:反编译需要修改的类(jad)
jad
命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,通过jad可以查看到应用正在运行的代码情况,这里我们使用jad 是为了确认线上某个类的运行的代码和我预期的一致,如果不一致需要修改或者增加代码则需通过后面的指令完成。
指令:jar 类全路径名
jad com.xx.xx.xx.TestController
第二步:把修改完的类动态加载到JVM(mc 和redefine)
如果我们需要修改正在运行中的应用代码,那么我们可以先准备好已经修改好的类,然后使用redefine指令把最新的代码编译加载到JVM内存里,从而达到不停应用程序修改业务代码的目的。
1、编译.java
文件生成.class
bash
mc /tmp/TestController.java
2、加载外部的.class
到 jvm。
arduino
redefine /tmp/TestController.class
3-3、其他命令介绍
详细使用参考官网:arthas.aliyun.com/doc/a
CMS GC 要点
如何调优 JVM 参数就成了一个绕不过的话题。因此,为了寻求一个 CMS GC 的 JVM 合理参数配置,笔者参考多篇社区文章及相关博客,总结了一些 CMS 相关的知识点,以及一套基于 CMS 的 JVM 参数配置。
CMS(Concurrent Mark Sweep,并发-标记-清除)是目前最常用的 JVM 垃圾回收器,这里不解释 CMS 的工作过程,只记录一些基础要点以帮助理解后面的内容:
- CMS 是一种基于并发、使用标记清除算法的垃圾回收器。CMS 会尽可能让 GC 线程与用户线程并发执行,可以消除长时间的 GC 停顿(STW)。
- CMS 不会对新生代做垃圾回收,默认只针对老年代进行垃圾回收。此外,CMS 还可以开启对永久代的垃圾回收(或元空间),避免由于 PermGen 空间耗尽带来 Full GC,JDK6以上受参数 -XX:+CMSClassUnloadingEnabled 控制,这个参数在 JDK8 之前默认关闭,JDK8 默认开启了。
- CMS 要与一个新生代垃圾回收器搭配使用,所谓"分代收集"。能与 CMS 配合工作的新生代回收器有 Serial 收集器和 ParNew 收集器,我们一般使用支持多线程执行的 ParNew 收集器。
- 使用 CMS GC 策略时,GC 类别可以分为:Young GC(又称 Minor GC),Old GC(又称 Major GC、CMS GC),以及Full GC。其中 Full GC 是对整个堆的垃圾回收,STW 时间较长,对业务影响较大,应该尽量避免 Full GC。
JVM 参数配置
经过理解各个参数的含义及取值影响,总结了以下的 JVM 参数配置,可以几乎不用调整使用:
ruby
-Xmx32g -Xms32g -Xmn1g -Xss256k-XX:SurvivorRatio=2
-XX:MaxPermSize=256m
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:ParallelGCThreads=10
-XX:ParallelCMSThreads=16
-XX:+CMSParallelRemarkEnabled
-XX:MaxTenuringThreshold=15
-XX:+UseCMSCompactAtFullCollection
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70
-XX:+CMSClassUnloadingEnabled
-XX:-DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps-Xloggc:/app/log/hbase/gc-hbase-REGIONSERVER-`hostname`-`date +%Y%m%d`.log
复制
如果是 64G 及以上的大堆,-Xmn 可以调整到2g,其他参数不变或微调。下面对一些重要的 JVM 参数介绍说明。
重点参数解析
以下参数解析都建立在使用 CMS GC 策略基础上,这里使用 CMS GC 表示老年代垃圾回收,Young GC 表示新生代垃圾回收。
① -Xmx, -Xms, -Xmn
-Xmx、-Xms 分别表示 JVM 堆的最大值,初始化大小。-Xmx 等价于-XX:MaxHeapSize,-Xms 等价于-XX:InitialHeapSize。
-Xmn表示新生代大小,等价于-XX:MaxNewSize、-XX:NewSize,这个参数的设置对 GC 性能影响较大,设置小了会影响 CMS GC 性能,设置大了会影响 Young GC 性能,建议取值范围在1~3g,比如32g堆大小时可以设为1g,64g堆大小时可以设为2g,通常性能会比较高。
② -Xss
表示线程栈的大小,等价于-XX:ThreadStackSize,默认1M,一般使用不了这么多,建议值256k。
③ -XX:SurvivorRatio
新生代中 Eden 区与 Survivor 区的比值,默认8,这个参数设置过大会导致 CMS GC 耗时过长,建议调小,使得短寿对象在Young区可以被充分回收,减少晋升到Old区的对象数量,以此提升 CMS GC 性能。
④ -XX:+UseParNewGC, -XX:+UseConcMarkSweepGC
分别表示使用并行收集器 ParNew 对新生代进行垃圾回收,使用并发标记清除收集器 CMS 对老年代进行垃圾回收。
⑤ -XX:ParallelGCThreads, -XX:ParallelCMSThreads
分别表示 Young GC 与 CMS GC 工作时的并行线程数,建议根据处理器数量进行合理设置。
⑥ -XX:MaxTenuringThreshold
对象从新生代晋升到老年代的年龄阈值(每次 Young GC 留下来的对象年龄加一),默认值15,表示对象要经过15次 GC 才能从新生代晋升到老年代。设置太小会严重影响 CMS GC 性能,建议默认值即可。
⑦ -XX:+UseCMSCompactAtFullCollection
由于 CMS GC 会产生内存碎片,且只在 Full GC 时才会进行内存碎片压缩(因此 使用 CMS 垃圾回收器避免不了 Full GC)。这个参数表示开启 Full GC 时的压缩功能,减少内存碎片。
⑧ -XX:+UseCMSInitiatingOccupancyOnly ,
-XX:CMSInitiatingOccupancyFraction
-XX:CMSInitiatingOccupancyFraction 表示触发 CMS GC 的老年代使用阈值,一般设置为 70~80(百分比),设置太小会增加 CMS GC 发现的频率,设置太大可能会导致并发模式失败或晋升失败。默认为 -1,表示 CMS GC 会由 JVM 自动触发。
-XX:+UseCMSInitiatingOccupancyOnly 表示 CMS GC 只基于 CMSInitiatingOccupancyFraction 触发,如果未设置该参数则 JVM 只会根据 CMSInitiatingOccupancyFraction 触发第一次 CMS GC ,后续还是会自动触发。建议同时设置这两个参数。
⑨ -XX:+CMSClassUnloadingEnabled
表示开启 CMS 对永久代的垃圾回收(或元空间),避免由于永久代空间耗尽带来 Full GC。
在线工具
- GC在线参数检查与优化:xxfox.perfma.com
- GC在线日志分析:gceasy.io/