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实现的锁;
  • -Fjstack <pid>无反应时,强制进行thread dump;
  • -mmixed形式列举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

原因:

  1. 需要在heap中为新对象分配空间时,发现空间不足,出现OOM。
  2. 将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方法导致。

引用资料