碰到问题,首先要做的就是基于数据进行定位问题,比如:程序运行日志、异常堆栈信息、GC日志记录、线程快照文件、堆内存快照文件等。同时,数据的收集又离不开监控工具的辅助,所以当 JVM 在线上运行过程中出现问题后,自然避免不了使用一些 JDK 自带以及第三方提供的工具,如:jps、jstat、jstack、jmap、jhat、hprof、jinfo、arthas等。
jps、jstat、jstack、jmap、jhat、jinfo等命令都是安装 JDK 后自带的工具,它们的功能主要是调用%JAVA_HOME%/lib/tools.jar包里面的Java 方法来实现的,所以如果你想自己打造一个属于自己的 JVM 监控系统,那在 Java 程序内部调用该jar包的方法即可实现。
JDK 官方提供的 JDK 工具参考文档,当然,如果你不会使用这些工具,也可以通过参数:tool -help来查看它的使用方法,如:jps -help。
进程监控工具 - jps
jps工具主要作用是用查看机器上运行的Java进程,类似于Linux系统的ps -aux | gerp java 命令。jps工具也支持查看其他机器Java进程,命令格式如下:
[root@clearwind ~]# jps -h
usage: jps [--help]
jps [-q] [-mlvV] [<hostid>] #hostId 是用来连接其他机器查看Java进程的远程ID
Definitions:
<hostid>: <hostname>[:<port>]
-? -h --help -help: Print this help message and exit.
-q : 查看机器所有隐形的Java进程,但只显示进程号
-m : ~,只显示传递给main方法的参数
-l : ~, 只显示运行的程序主类的包名,或者运行程序jar包的完成路径
-v : ~, 单独显示JVM启动时,显示指定的参数
-V : ~, 显示主类名或者jar包名
配置信息查看工具 - jinfo
jinfo工具主要用于实时查看JVM的运行参数、在运行时动态的调整参数
[root@clearwind ~]# jinfo -h
Usage:
jinfo <option> <pid>
(to connect to a running process)
where <option> is one of:
-flag <name> to print the value of the named VM flag #输出与指定名称``对应的所有参数,以及参数值。
-flag [+|-]<name> to enable or disable the named VM flag #开启或者关闭与指定名称``对应的参数
-flag <name>=<value> to set the named VM flag to the given value #设置与指定名称``对应参数的值
-flags to print VM flags #输出 JVM 全部的参数
-sysprops to print Java system properties #输出 JVM 全部的系统属性
<no option> to print both VM flags and system properties #第一个参数不写,默认输出 JVM 的全部参数和系统属性。
-? | -h | --help | -help to print this help message
信息统计监控工具 - jstat
jstat全称“Java Virtual Machine statistics monitoring tool”,该工具可以利用JVM内建的指令对Java程序的资源以及性能进行实时的命令行监控,监控范围包含:对空间的各数据区、垃圾回收状况以及类的加在与卸载状态。
[root@clearwind ~]# jstat -h
Usage: jstat --help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
Definitions:
<option> An option reported by the -options option
<vmid> Virtual Machine Identifier. A vmid takes the following form:
<lvmid>[@<hostname>[:<port>]]
Where <lvmid> is the local vm identifier for the target
Java virtual machine, typically a process id; <hostname> is
the name of the host running the target Java virtual machine;
and <port> is the port number for the rmiregistry on the
target host. See the jvmstat documentation for a more complete
description of the Virtual Machine Identifier.
<lines> Number of samples between header lines.
<interval> Sampling interval. The following forms are allowed:
<n>["ms"|"s"]
Where <n> is an integer and the suffix specifies the units as
milliseconds("ms") or seconds("s"). The default units are "ms".
<count> Number of samples to take before terminating.
-J<flag> Pass <flag> directly to the runtime system.
-? -h --help Prints this help message.
-help Prints this help message.
jstat -gc -t -h30 9895 1s 300
-gc:监控 GC 的状态
-t:显示系统运行的时间
-h30:间隔 30 行数据,输出一次表头
9895:Java 进程 ID
1s:时间间隔
300:本次输出的数据行数
jstat参数解释
[option]: 监控参数选项。
-t:在输出结果中加上Timestamp列,显示系统运行的时间。
-h:可以在周期性数据输出的时候,指定间隔多少行数据后输出一次表头。
vmid:virtual Machine Ip虚拟 ID,也就是指定一个要监控的 Java 进程 ID
interva1:每次执行的间隔时间,默认单位为ms。
count:用于指定输出多少条数据,默认情况下会一直输出。
执行命令jstat -option后,可以看到存在很多选项,如下:
-class:输出类加载classLoad相关的信息。
-compiler:显示与 JIT 即时编译相关的信息。
-gc:显示与 GC 相关的信息。
-gccapacity:显示每个分代空间的容量以及使用情况。
-gcmetacapacity:输出元数据空间相关的信息。
-gcnew:显示新生代空间相关的信息。
-gcnewcapacity:显示新生代空间的容量大小以及使用情况。
-gcold:输出年老代空间的信息。
-gcoldcapacity:输出年老代空间的容量大小以及使用情况。
-gcutil:显示垃圾回收信息。
jstat输出字段含义
堆内存统计分析工具 - jmap
jmap是一个多功能的工具,主要是用于查看堆空间的使用情况,通常会配合jhat工具一起使用,可以用于生成Java堆的Dump文件。但除此之外,也可以查看finalize队列、元数据空间的详细信息,Java 堆中对象统计信息,如每个分区的使用率、当前装配的 GC 收集器等
[root@clearwind ~]# jmap -h
Usage:
jmap -clstats <pid>
to connect to running process and print class loader statistics
jmap -finalizerinfo <pid>
to connect to running process and print information on objects awaiting finalization
jmap -histo[:[<histo-options>]] <pid>
to connect to running process and print histogram of java object heap
jmap -dump:<dump-options> <pid>
to connect to running process and dump java heap
jmap -? -h --help
to print this help message
dump-options:
live dump only live objects (takes precedence if both "live" and "all" are specified)
all dump all objects in the heap (default if one of "live" or "all" is not specified)
format=b binary format
file=<file> dump heap to <file>
gz=<number> If specified, the heap dump is written in gzipped format using the given compression level.
1 (recommended) is the fastest, 9 the strongest compression.
Example: jmap -dump:live,format=b,file=heap.bin <pid>
histo-options:
live count only live objects (takes precedence if both "live" and "all" are specified)
all count all objects in the heap (default if one of "live" or "all" is not specified)
file=<file> dump data to <file>
parallel=<number> Number of parallel threads to use for heap inspection:
0 (the default) means let the VM determine the number of threads to use
1 means use one thread (disable parallelism).
For any other value the VM will try to use the specified number of threads, but might use fewer.
Example: jmap -histo:live,file=/tmp/histo.data <pid>
jmap参数解析
jmap是一个多功能的Java诊断工具,主要用于分析和监控Java应用程序的内存使用情况,特别是针对堆空间。它与jhat等工具协同工作,能够生成和分析堆转储文件,帮助开发者识别内存泄漏、监控对象分配以及优化内存管理。以下是jmap的关键特性和使用方法的总结:
主要功能
生成堆转储:通过-dump选项,可以创建Java堆的快照,可以选择导出所有对象或仅导出存活对象,支持多种输出格式和压缩选项。
类加载器统计:使用-clstats选项,展示运行时进程中类加载器的统计信息,包括已加载的类数量等。
待终结对象信息:通过-finalizerinfo,列出等待终结(即将被垃圾回收)的对象,有助于理解对象生命周期管理。
堆内存对象统计:-histo选项提供堆中对象的统计直方图,包括对象数量和所占内存大小,可选参数包括统计存活对象或所有对象,并支持输出到文件。
使用示例
生成存活对象的堆转储:jmap -dump:live,format=b,file=Dump.hprof [pid]
查看类加载器统计:jmap -clstats [pid]
对象统计直方图:jmap -histo [pid] 或 jmap -histo:live [pid] 以区分统计存活对象或所有对象
jmap工具实际使用方式:jmap -clstats [pid]或jmap -dump:live,format=b,file=Dump.phrof [pid]等。
堆快照导出命令解析:
live:导出堆中存活对象快照;format:指定输出格式;file:指定输出的文件名及其格式(.dat、.phrof等格式)。
性能影响:执行jmap,尤其是生成堆转储操作,可能会引起目标应用暂停,对性能产生显著影响,应避免在生产环境频繁使用。
Attach机制:大部分 JDK 提供的工具与 JVM 通信方式都是通过的Attach机制实现的,该机制可以针对目标 JVM 进程进行一些操作,比如获取内存Dump、线程Dump、类信息统计、动态加载Agent、动态设置 JVM 参数、打印 JVM 参数、获取系统属性等。有兴趣可以去深入研究一下,具体源码位置位于:com.sun.tools.attach包,里面存在一系列Attach机制相关的代码。
替代工具:随着JDK版本演进,虽然jhat已被废弃,但仍有VisualVM、Eclipse Memory Analyzer (MAT)等现代工具可用于分析jmap生成的堆转储文件,提供更友好的界面和更丰富的分析功能。
堆内存快照分析工具 - jhat
jhat工具一般配合jmap工具使用,主要用于分析jmap工具导出的Dump文件,其中也内嵌了一个微型的HTTP/HTML服务器,所以当jhat工具分析完Dump文件后,可以支持在浏览器中查看分析结果。
不过在线上环境中一般不会直接使用jhat工具对Dump文件进行解析,因为jhat解析Dump文件,尤其是大体积的Dump时,是一个非常耗时且占用硬件资源的过程。所以为了防止占用服务器过多的资源,通常都会将Dump文件 copy 到其他机器或本地中分析。
不过话说回来,到了本地一般也不会使用jhat,因为分析之后生成的结果通过浏览器观察时很难看,一般都会选择 MAT(Eclipse MemoryAnalyzer)、IBM HeapAnalyzer、VisualVM、Jprofile等工具。
jhat`命令格式:`jhat [-stack ] [-refs ] [-port ] [-baseline ] [-debug ] [-version] [-h|-help]
jhat的这条指令有点长,其中可以选择填写很多参数,释义如下!
-stack:默认为true,是否开启对象分配调用栈跟踪。
-refs:默认为true,是否开启对象引用跟踪
-port:默认为7008,设置jhat工具浏览器访问的端口号,
-baseline:指定基准堆转储pump文件,在两个ump文件中有相同对象时,会被标记为旧对象,不同的对象会被标记为新对象,主要用于对比分析两个不同的Dump文件。
-debug:默认为e,设置 debug 级别,0 表示不输出调试信息,值越大信息越详细。-version:显示版本信息
-help:查看帮助信息。
``:要分析的Dump文件。
-J:jhat工具实际上也是启动了一个 JVM 进程来执行的,可以通过-J指令为该 JVM 传递一些 JVM 参数,如:-J-Xmx128m这类的。
[root@clearwind ~]#
堆栈跟踪工具 - jstack
jstack工具主要用于捕捉 JVM 当前时刻的线程快照,线程快照是 JVM 中每条线程正在执行的方法堆栈集合。在线上情况时,生成线程快照文件可以用于定位线程出现长时间停顿的原因,如线程死锁、死循环、请求外部资源无响应等等原因导致的线程停顿。
当线程出现停顿时,可以通过jstack工具生成线程快照,从快照信息中能查看到 Java 程序内部每条线程的调用堆栈情况,从调用堆栈信息中就可以清晰明了的看出:发生停顿的线程目前在干什么,在等待什么资源等。
同时,当 Java 程序崩溃时,如果配置好了参数,生成了core文件,咱们也可以通过jstack工具从core文件中提取 Java 虚拟机栈相关的信息,从而进一步定位程序崩溃的原因。
[root@10-23-121-225 ~]# jstack -h
Usage:
jstack [-l][-e] <pid>
(to connect to running process)
Options:
-l long listing. Prints additional information about locks
-e extended listing. Prints additional information about threads
-? -h --help -help to print this help message
jstack`工具命令格式:`jstack [-F] [option1] [option2]
-l (long listings): 打印额外的锁信息,包括所属的类名、锁的状态以及持有该锁的线程等,有助于分析死锁。
-m: 打印Java和Native C/C++堆栈信息,对于涉及JNI调用的线程问题尤其有用。
-F: 强制打印堆栈信息,即使JVM不响应(挂起或死锁)。这在目标JVM没有响应时非常有用,但可能不那么精确,因为它依赖于操作系统信号。
-h | -help: 打印帮助信息。
<pid>: 目标Java进程的ID。
实际应用场景
线程死锁检测:当怀疑程序中存在线程死锁时,使用jstack可以迅速定位死锁线程及其持有的锁资源,从而找到问题根源。
性能瓶颈分析:通过分析线程堆栈,可以识别出哪些线程长时间处于等待状态或CPU密集运算,进而优化程序逻辑或调整资源分配。
异常崩溃分析:当Java应用崩溃并生成了core dump文件,结合jstack分析core文件,可以获取崩溃时各线程的调用栈,这对于理解崩溃原因至关重要。
诊断活锁和饥饿:除了死锁,jstack也能帮助识别活锁(线程相互礼让无法进展)和线程饥饿(线程因资源竞争无法获得执行机会)的情况。
使用示例
普通使用:jstack <pid>,获取指定Java进程的线程堆栈信息。
处理无响应JVM:jstack -F <pid>,当JVM不响应时强制打印堆栈信息。
详细锁信息:jstack -l <pid>,获取包括锁信息在内的详细堆栈跟踪。
同时,jstack工具的-F参数与jmap的作用相同,当正常执行失效时,加上-F可以强制执行jstack指令。
最后,jstack工具导出的Dump日志值得留意的状态:
JVM排查工具小结
除了JDK自带的一系列监控和诊断工具(如jmap、jhat、jstack、jconsole等)之外,还有许多第三方工具在Java应用的性能监控、故障诊断方面表现得更为出色,它们提供了更为丰富和用户友好的功能。以下是一些常用的第三方工具及其特点概述:
Arthas: 面向Java开发者的一款开源诊断工具,无需重启应用即可进行代码级别的诊断,支持方法追踪、内存查看、性能统计、JVM信息查询等功能,极大地方便了线上问题的即时定位和诊断。
JProfiler: 是一款商业的全功能Java剖析工具,提供了CPU、内存、线程分析、锁 contention分析等强大功能,界面友好,特别擅长于复杂性能问题的定位,支持远程连接和离线分析。
YourKit: 另一款强大的Java剖析工具,同样涵盖了CPU、内存、线程分析等多方面,以其高性能、低开销和详尽的报告著称,支持实时监控和离线分析,适合于开发和生产环境。
VisualVM: 虽然VisualVM最初是JDK的一部分,但现在作为一个独立项目维护,它集成了多个JDK工具的功能,提供了内存、CPU、线程监控,以及堆和方法区的分析能力,同时支持插件扩展,界面直观,易于上手。
MAT (Eclipse Memory Analyzer Tool): 专注于Java堆内存分析,能高效处理大型转储文件,帮助发现内存泄漏、分析内存消耗,提供丰富的报告和图形化展示,是解决内存问题的得力助手。
Perf4J: 一个Java性能监控库,用于收集和展示应用的运行时性能指标,支持日志记录和SLF4J,方便集成到现有应用中进行性能监控和趋势分析。
JConsole: 虽然是JDK自带工具,但值得单独提及,因为它提供了简单的GUI界面来监控Java应用的资源使用情况,包括内存、CPU、线程和类加载信息,适合初步诊断和监控。
Perfino: 一个轻量级的性能监控工具,专注于收集和报告应用的性能数据,支持自定义监控点,提供详细的性能报告和警报功能,适用于生产环境的持续监控。
JProbe: 一款商业性能分析工具,提供内存和CPU分析、代码覆盖分析等功能,帮助开发者理解应用的行为并优化性能。
这些第三方工具各有千秋,选择合适的工具取决于具体的需求、预算、以及对监控深度和广度的要求。它们通常提供了更高级的分析能力和更佳的用户体验,是提升开发效率和保障应用稳定运行的重要辅助。