JVM调优实战 - JVM诊断工具与问题定位排查总结
1. JVM背景
总结排查JVM问题时可以使用的工具、策略、措施等。
2. JVM配置
2.1. oom时自动dump heap
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=
存放dump的目录
2.2. 持续性的JFR
JFR Java flight recording java运行记录。当应用出问题时,JFR可以提供最近一段时间的dump data。JFR对很多问题都能提供帮助,如memory leaks to network errors, high CPU usage, thread blocks
。
命令行
允许jmc操作
-XX:+UnlockCommercialFeatures -XX:+FlightRecorder
Start a profiling recording
-XX:StartFlightRecording=delay=20s,duration=60s,name=myrecording,filename=C:\TEMP\myrecording.jfr,settings=profile
Start a continuous recording
-XX:FlightRecorderOptions=defaultrecording=true,disk=true,repository=/tmp,maxage=6h,settings=default
jcmd指令
2.3. 输出GC日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintHeapAtGC -Xloggc:存储gclog的文件路径
-verbosegc
输出内容少,无法看到各个代的回收情况
2.4. JMX远程监控
应用启动时开启JMX
-Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false
应用启动之后,独立开启JMX。
jcmd pid ManagementAgent.start options
3. 收集数据
log
heap dump
JFR
thread stack
4. 诊断工具
诊断工具分类:
- 事后诊断 :用于应用crash之后诊断问题
- 悬停\死机进程诊断 :用于死机或死锁应用的诊断
- 监控 :监控正在运行的应用
4.1. JMC
Oracle Java Mission Control 是一个用于对 Java 应用程序进行管理、监视、概要分析和故障排除的可视化工具套件。 首次安装时,Java Mission Control 包括 JMX 控制台和 Java 飞行记录器。从 Mission Control 中可以轻松安装更多插件。
- JMX :在运行时监视和管理您的 Java 应用程序和 JVM。
- JFR :运行时JVM行为的时间序列数据并使用 Java 飞行记录器工具脱机分析记录。
记录类型
- 持续性记录 :对记录记性持续性存储,且可设置数据窗口,如保留最近6小时的记录。很低的性能影响。
- 信息采集记录 :一段时间内采集记录,约2%的性能影响。
应用场景
- 哪个方法使用的多,哪些对象创建的多;
- 哪些类使用了越来越多的heap,导致memory leak;
- 寻找瓶颈,如同步;
必须通过jmx端口连接到JVM。
4.2. jcmd
推荐使用,替换jmap、jstack、jinfo 。 必须在jvm运行的机器中执行,用于向jvm发送诊断指令请求,可用于控制JFR、定位问题、诊断jvm和应用等。
用法
- jcmd 列举本机jvm进程ID和名称
- jcmd pid help 列举该jvm进程可使用的指令
- jcmd pid help command 列举指令参数
指令
Thread.print|
列举线程栈,同jstack。
参数-l 显示获取的lock接口实现的锁
---|---
GC.heap_info|
heap信息,列举各代垃圾回收器类型、容量、占用量,类似jmap -heap,但没有jmap -heap全面
清楚列举GC类型,heap容量信息,regin信息,Metaspace容量信息,以及归属于Metaspace的compressed class space(CCS)的容量信息。
CMS列举各代垃圾回收器类型、容量、占用量。
GC.heap_dump|
GC.heap_dump [options]
参数-all=true/false dump所有信息
GC.class_stats| java class 元数据的统计,需要配置-XX:+UnlockDiagnosticVMOptions。
GC.finalizer_info| 类似jmap -finalizerinfo
GC.class_histogram| class实例heap占用情况统计,类似jmap -histo
VM.classloader_stats|
classloader统计 类似jmap -clstats
可以看到类加载器的层次关系
VM.uptime| jvm进行持续运行时间
VM.flags| jvm配置信息
VM.command_line| 启动jvm时使用的命令行信息
VM.system_properties| 系统变量
JFR.start| 开启JFR采集,并指定存储文件,之后可使用JMC打开该文件
JFR.check| 列举当前jvm正在执行的JFR采集
JFR.stop| 停止某个JFR采集
JFR.dump| 停止某个JFR采集,并且将采集记过dump到某个文件,之后可使用JMC打开该文件
GC.heap_info显示结果如下:
garbage-first heaptotal 196608K,used 50473K [0x0000000701000000, 0x0000000701100600, 0x00000007c0000000)
region size 1024K, 47 young (48128K), 2 survivors (2048K)
Metaspaceused 15318K, capacity 15616K, committed 15872K, reserved 1062912K
class spaceused 1840K,capacity 1931K, committed 2048K,reserved 1048576K
4.3. JConsole
4.4. jdb
command-line debugger,使用java debug interface(JDI)去启动或连接到目标JVM。
JDI作为JPDA(Java Platform Debugger Architecture)的一个组件,为需要访问JVM运行状态的debugger或类似系统提供可用信息。
通过连接器与目标jvm进行交互,远程JVM需要使用SA Debug Server。
4.5. jinfo
配置信息、系统变量、启动项等,建议使用jcmd
4.6. jmap
heap容量配置等概要、dump等。
用法:
jmap [option] <pid> 连接到正在运行的vm
jmap [option] <executable <core> 连接core文件
jmap [option] [server_id@]<remote server IP or hostname> 连接远程vm
4.7. jstat
提供JVM的表现信息以及应用的资源消耗信息,如与垃圾回收相关的统计信息,代相关信息,用来诊断表现性问题,heap大小问题,gc问题。
用法:
jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
选项作用:
-t
表头显示Timestamp,即vm运行的总时间-h<lines>
表头之间显示样本行数,如-h2 2个样表生成一个表头- vmid 即pid
- interval 多久输出一条样本记录,可用单位ms s,默认ms
- count 输出count条样本记录后,采集自动结束。
4.8. jstack
用于输出虚拟机当前所有线程栈信息,包括java线程、VM内部线程、本地线程,并且可侦测死锁。
用法:
连接正在运行的进程 jstack [-l] <pid>
强制连接无反应的进程 jstack -F [-m] [-l] <pid>
连接core文件 jstack [-m] [-l] <executable> <core>
连接远程进程 jstack [-m] [-l] [server_id@]<remote server IP or hostname>
选项作用:
- 不加option 详细列举栈帧,死锁侦测结果;
-l
详细列举栈帧,死锁侦测结果,是否已获取Lock实现的锁;-F
当jstack <pid>
无反应时,强制进行thread dump;-m
mixed形式列举java和本地线程栈帧,死锁侦测结果。内容不详细,不易理解。
4.9. 其他第三方
Eclipse Memory Analyzer Tool (MAT)、NetBeans Profiler
5. 诊断问题
5.1. 判断memory leak
- 使用JFR :使用JFR可以推断和阻止memory leak。针对一段时间的JFR采集数据,对比old代gc后heap的占用情况,若heap使用量是持续增加的,说明可能导致memory leak。
- JMC观察 * 内存-概览观察内存使用量图 * 内存-垃圾回收器,对比oldGC后heap占用量
- jstat :gc后old占用量虽然减少,但是对比之前的结果,占用量是持续增加的。
5.2. 定位引起leak的位置
通过查看class实例占用情况,基本发现占用空间最多的都是一些基本类型,如char,所以通过占用量无法直接定位具体类。
JMC观察 :内存-分配,关注压力和堆栈
其他方式 :可以查看占用量大的对象的gc root,然后查看引用量大的gc root
5.3. OutOfMemoryError
原因:
- 需要在heap中为新对象分配空间时,发现空间不足,出现OOM。
- 将class加载到本地内存时,本地内存(metaspace)不足,出现OOM。
解决OOM前,需要OOM异常信息末尾的提示,明确是heap不足还是本地heap不足。
java.lang.OutOfMemoryError: Java heap space
配置的heap过小,无法满足app的足迹要求。
对于已经运行近较长时间的应用,可能是memory leak问题。
应用创建很多优先级很高的线程,导致finalizer demaon线程处理the finalization queue的速度不及增长速度。对于提供finalize方法的class,其对象的空间不会在gc期间被清理。而是放到the finalization queue中,由finalizer demaon线程进行处理。
java.lang.OutOfMemoryError: GC Overhead limit exceeded
原因 :java进程使用超过98%的时间进行GC,且GC后heap清除率低于2%,且至少连续执行5次GC。表示lived data占用空间过多,导致仅有极少空间用于分配。
措施 :增大heap;关闭上线限制 -XX:-UseGCOverheadLimit
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
原因 :创建数组所需空间超过heap可用空间
措施 :是否有必要创建大数组,增大heap。
java.lang.OutOfMemoryError: Metaspace
java class 元数据有很多种类,-klass类型的原数据占用的native memory称为CompressedClassSpace,其类型的原数据占用的native memory称为metaspace。
措施 :
-XX:MaxMetaspaceSize=xx
java.lang.OutOfMemoryError: Compressed class space
On 64-bit platforms, a pointer to class metadata can be represented by 32-bit offset (with UseCompressedOops).
-XX: CompressedClassSpaceSize=xx
java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?
an allocation from the native heap failed and the native heap might be close to exhaustion
java.lang.OutOfMemoryError: reason stack_trace_with_native_method
由JNI或native方法导致。