Skip to the content.

首页

监控&调优


JVM通用参数


垃圾收集器参数

参数 描述
UseConcMarkSweepGC 使用ParNew + CMS + Serial Old的收集器组合
SurvivorRatio 新生代中Eden区域与Survivor区域的容量比值,默认值为8,即8:1:1
PretenureSizeThreshold 直接进入老年代的对象大小
MaxTenuringThreshold 晋升到老年代的对象年龄
HandlePromotionFailure 是否允许空间分配担保失败
CMSInitiatingOccupancyFration 设置CMS在老年代空间使用多少后触发垃圾收集,默认68%
UseG1GC 使用G1收集器,JDK9之后服务端模式的默认值
G1HeapRegionSize 设置Region大小,非最终值
MaxGCPauseMills 设置G1收集过程目标时间,默认200ms,非硬性条件
MaxTenuringThreshold 晋升到老年代的对象年龄
MaxTenuringThreshold 晋升到老年代的对象年龄
G1NewSizePercent G1新生代最小值,默认5%
G1MaxNewSizePercent G1新生代最大值,默认60%
G1MaxNewSizePercent G1新生代最大值,默认60%
G1ReservePercent G1老年代为分配担保预留的空间比例,默认10%
InitiatingHeapOccupancyPercent 设置触发全局并发标记的老年代在Java堆占用率阈值,默认值45%
ParallelGCThreads 用户线程冻结期间(STW阶段)并行执行的收集器线程数,默认值是CPU核心数
ConGCThreads 并发整理、并发标记(非STW阶段)的执行线程数,默认值由ParallelGCThread计算得出,且小于其值

垃圾收集器日志参数

JDK9之后日志相关参数全部归纳到“-Xlog”上,垃圾收集器的标签为gc。

描述 JDK9之前参数 JDK9之后参数
查看GC基本信息 -XX:+PrintGC -Xlog:gc
查看GC详细信息 -XX:+PrintGCDetails -Xlog:gc*
查看GC前后的堆、方法区可用高容量变化 -XX:+PrintHeapAtGC -Xlog:gc+heap=debug
查看熬过收集后剩余对象的年龄分布信息 -XX:+PrintTenuringDistribution -Xlog:gc+age=trace
查看GC过程中用户线程并发时间 -XX:+PrintGCApplicationConcurrentTime -Xlog:safepoint
查看GC过程中用户线程停顿时间 -XX:+PrintGCApplicationStoppedTime -Xlog:safepoint
查看安全点统计信息 -XX:+PrintSafepointStatistics -Xlog:safepoint
查看安全点统计信息 -XX:PrintSafepointStatisticsCount=1 -Xlog:safepoint
查看收集器Ergonomics机制自动调节的相关信息 -XX:+PrintAdaptiveSizePolicy -Xlog:gc+ergo*=trace
查看Reference处理信息 -XX:+PrintReferenceGC -Xlog:gc+age=trace

gc日志输出

# GC日志输出的文件路径
-Xloggc:/path/to/gc-%t.log
# 开启日志文件分割
-XX:+UseGCLogFileRotation 
# 最多分割几个文件,超过之后从头文件开始写
-XX:NumberOfGCLogFiles=5
# 每个文件上限大小,超过就触发分割
-XX:GCLogFileSize=20M
# OOM时输出堆转储文件
-XX:+HeapDumpOnOutOfMemoryError
-XX:+HeapDumpPath=/{path}/heapdump.hprof

故障处理工具

jps

列出正在运行的虚拟机进程,并显示虚拟机执行主类名称,以及这些进程的本地虚拟机唯一ID(LVMID)。

jps [options] [hostid]
选项 作用
-q 只输出LMVID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是JAR包,则输出其路径
-v 输出虚拟机进程启动时的JVM参数

jstat

监视虚拟机各种运行状态信息,可以显示虚拟机进程中的类加载、内存、垃圾收集、即时编译等运行时数据。

jstat [ option vmid [interval [count]] ]
# interval和count代表查询间隔和次数,如果省略这两个参数则表示只查询一次。
选项 作用
-class 监视类加载、卸载数量、总空间、类装载耗时等
-compiler 输出即时编译器的行为信息
-printcompilation 输出已被即时编译的方法
-gc 监视Java堆状况,包括各区容量、已用空间、垃圾收集时间等
-gccapacity 与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间
-gcutil 与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause 与-gcutil一样,但是会额外输出导致上一次垃圾收集产生的原因
-gcnew 监视新生代的垃圾收集情况
-gcnewcapacity 与-gcnew基本相同,但输出关注使用到的最大、最小空间
-gcold 监视老年代的垃圾收集情况
-gcoldcapacity 与-gcold基本相同,但输出关注使用到的最大、最小空间
-gcmetacapacity 输出元空间使用到的最大、最小空间

jinfo

实时查看和调整虚拟机各项参数。

jinfo [option] pid

jstack

用于生成虚拟机当前时刻的线程快照,即每一条线程正在执行的方法堆栈的集合。用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间挂起等。

jstack [option] vmid
选项 作用
-F 强制输出线程堆栈
-l 额外输出锁的相关信息
-m 如果调用到本地方法,可以显示C/C++的堆栈

jmap

用于生成堆转储快照(dump文件),也可以查询Java堆相关的各类信息。dump文件可以使用jhat、Visual VM等图形化工具进行分析。

jmap [option] vmid
选项 作用
-heap 显示堆详细信息,如使用的收集器、参数配置、分代状况等
-histo 显示队中对象统计信息,包括类、实例数量、合计容量
-clstats 显示元空间中类加载器的统计信息
-dump 生成dump文件
-F 强制生成dump快照,不支持live参数
-dump:[live,]format=b,file=<fileName>,[parallel=<number>]
#dump命令格式,live参数表示只dump存活对象,parallel参数表示并行遍历堆的线程数。

jhat

jdk内置工具(JDK9后移除),用于分析堆转储文件,执行jhat命令会启动一个web服务器,默认端口为7000。

jhat [option] <file>

jvisualvm

Java自带的JVM监控工具(JDK14后移除),也可用于分析堆转储文件。

jcmd

JDK7开始提供,是集成式的多功能工具箱。

基础工具 JCMD
jsp -lm jcmd
jmap -dump <pid> jcmd <pid> GC.heap_dump
jmap -histo <pid> jcmd <pid> GC.class_histogram
jstack <pid> jcmd <pid> Thread.print
jinfo -sysprops <pid> jcmd <pid> VM.system_properties
jinfo -flags <pid> jcmd <pid> VM.flags

NMT(native memory tool)

# 开启,detail会有10%-15%的性能损失
-XX:NativeMemoryTracking=[off | summary | detail]

# 查询分析
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]

调优

收集器选择

确定应用程序关注的重点,吞吐量、停顿时间、内存占用、基础设施、JDK版本等,选择最合适的垃圾收集器。

核心指标

优化案例

大内存硬件上的文档服务

定时大文件数据分析导致停顿时间变长

safepoint导致长时间停顿

反射膨胀机制导致频繁Full GC


CPU飙升排查

常见原因包括:while死循环、频繁创建对象、超多线程调度、外部系统命令调用。

  1. 使用【top】命令找出占用CPU高的进程;
  2. 使用【top -Hp [PID]】命令找到该进程中CPU占用最高的线程;
  3. 使用【printf “%x” <TID>】将十进制的TID转换为16进制;
  4. 使用【jstack <PID> | grep <TID>】查看堆栈快照信息。

系统优化

通过链路追踪工具确定系统的瓶颈所在,如果是数据库瓶颈,则判断是否需要优化索引、是否需要引入缓存、是否需要分库分表;如果确实是硬件瓶颈了则需要考虑机器扩容;扩容前还得先尝试优化,首先从应用层面优化,快速失败、填谷削峰、算法优化、使用缓存等,然后对JVM调优以提高吞吐量,最后尝试从网络和操作系统排查。