进行jvm内存分析可以排查存在和潜在的问题。
通过借助jdk自带的常用工具,可以分析大概可能的问题定位以及确定优化方向。
JVM内存分析有很多好处。
本文将通过一次jvm内存分析过程来说明jps
、jcmd
、jstat
、jstack
和 jmap
工具的使用方法。
本文使用到的是JDK17版本。
当需要进行 JVM 内存分析时,结合使用 jps
、jcmd
、jstat
、jstack
和 jmap
可以提供全面的诊断信息。
下面是一般的步骤:
使用 jps
查看 Java 进程的 PID:bash
代码解读复制代码jps -l
这将列出所有 Java 进程的 PID 和主类名。
使用 jcmd
执行一些诊断命令:bash
代码解读复制代码jcmd <PID> VM.flags
jcmd <PID> VM.system_properties
这些命令可以显示 JVM 的启动参数和系统属性,帮助了解 JVM 的配置。
使用 jstat 监视 JVM 内存和垃圾回收情况:bash
代码解读复制代码jstat -gc <PID> 5000 10
这将持续输出 JVM 的垃圾回收情况,包括各个堆区的使用情况、GC 时间等。
使用 jstack
生成线程堆栈信息:bash
代码解读复制代码jstack <PID>
查看线程堆栈信息,以检查是否存在死锁或其他线程相关的问题。
使用 jmap
生成堆转储文件:bash
代码解读复制代码jmap -dump:file=heapdump.hprof <PID>
这将生成一个名为 heapdump.hprof
的堆转储文件,可以用于进一步分析内存使用情况,查找内存泄漏等问题。
分析堆转储文件:
使用工具如 Eclipse Memory Analyzer (MAT) 或者 VisualVM 来分析生成的堆转储文件,查找内存泄漏、大对象、无用对象等问题。
通过结合使用这些工具,可以全面地了解 JVM 运行时的状态,诊断性能问题,以及解决内存相关的错误。
下面将详细解释这些工具的使用方法。
jps
是 JDK 提供的一个用于列出 Java 虚拟机进程的命令行工具。它通常用于查看当前系统中正在运行的 Java 进程的 PID(进程标识符)以及对应的主类名。
下面是 jps
命令的使用方法:
或者使用ps -ef|grep java
也可以搜索到对应的pid。bash
代码解读复制代码jps [ options ] [ hostid ]
其中,options
是一些可选的参数,hostid
是可选的主机标识符。常用的选项包括:
-q
:仅显示进程的 PID,不显示对应的主类名。-m
:显示传递给主类的参数。-l
:显示主类的全限定名,通常用于区分具体的 Java 应用程序。-v
:显示传递给 JVM 的参数。例如,要显示当前系统中所有 Java 进程的 PID 和对应的主类名,可以直接运行 jps
命令:bash
代码解读复制代码jps
如果要仅显示 PID,可以使用 -q
选项:bash
代码解读复制代码jps -q
要显示主类的全限定名,可以使用 -l
选项:bash
代码解读复制代码jps -l
如果要显示传递给主类的参数,可以使用 -m
选项:bash
代码解读复制代码jps -m
如果要显示传递给 JVM 的参数,可以使用 -v
选项:bash
代码解读复制代码jps -v
jcmd:jcmd 命令是 Java 8 新增的命令,可以执行多种 JVM 监控和诊断任务。例如,可以使用 jcmd <pid> VM.flags
查看 JVM 启动参数,或者使用 jcmd <pid> Thread.print
打印线程堆栈信息。
下面是 jcmd
命令的基本使用方法:bash
代码解读复制代码jcmd <PID | main class> <command> [options]
其中:
<PID | main class>
:要操作的 Java 进程的 PID(进程标识符)或者主类名。如果提供了 PID,则直接操作对应的 Java 进程;如果提供了主类名,则 jcmd
会尝试找到匹配的 Java 进程并执行相应的命令。<command>
:要执行的诊断命令。[options]
:可选的命令参数。常用的 jcmd
命令包括:
jcmd
支持的命令列表,以及每个命令的简要描述。举例来说,如果要打印指定 Java 进程的线程堆栈信息,可以使用以下命令:bash
代码解读复制代码jcmd <PID> Thread.print
如果要执行一次垃圾回收,可以使用以下命令:bash
代码解读复制代码jcmd <PID> GC.run
如果要生成 Java 堆转储文件,可以使用以下命令:bash
代码解读复制代码jcmd <PID> GC.heap_dump <filename>
jstat:jstat 命令可以监视 JVM 内存、垃圾回收等情况。例如,可以使用 jstat -gc <pid>
查看垃圾回收统计信息,或者使用 jstat -gcutil <pid>
查看垃圾回收统计信息及内存使用情况。
下面是 jstat
的基本使用方法:bash
代码解读复制代码jstat [ options ] <PID> [ interval [ count ] ]
其中:
[ options ]
:可选的命令选项,用于指定要监视的数据类型和格式。<PID>
:要监视的 Java 进程的 PID(进程标识符)。[ interval ]
:可选参数,指定输出统计信息的时间间隔(以毫秒为单位)。如果省略,则默认为每秒一次。[ count ]
:可选参数,指定输出统计信息的次数。如果省略,则默认为无限次输出。常用的 jstat
命令选项包括:
举例来说,要查看 Java 进程的类加载情况,可以使用以下命令:bash
代码解读复制代码jstat -class <PID>
如果想要每隔 5 秒输出一次类加载信息,共输出 10 次,可以使用以下命令:bash
代码解读复制代码jstat -class <PID> 5000 10
jstat只能查看当前的gc信息,查看gc日志更适合线上环境的做法是在启动JVM时加上-XX:+PrintGCDetails -Xloggc:/path/to/gc.log
(JDK1.8以下)或者-Xlog:gc*:file=/path/to/gc.log
(JDK9+)参数,将生成的gc日志文件放到GCViewer、GCeasy(需注册)进行分析。
jstack:jstack 命令用于生成 Java 线程转储快照,可以用于分析线程状态、死锁等问题。例如,可以使用 jstack <pid>
打印线程堆栈信息,或者使用 jstack -l <pid>
打印线程堆栈信息及锁信息。
下面是 jstack
命令的基本使用方法:bash
代码解读复制代码jstack [ options ] <PID>
其中:
[ options ]
:可选的命令选项,用于指定输出的格式等。<PID>
:要生成线程堆栈信息的 Java 进程的 PID(进程标识符)。常用的 jstack
命令选项包括:
举例来说,要生成指定 Java 进程的线程堆栈信息,可以使用以下命令:bash
代码解读复制代码jstack <PID>
如果想要输出长列表格式的线程堆栈信息,可以使用 -l
选项:bash
代码解读复制代码jstack -l <PID>
如果 Java 进程没有响应,可以使用 -F
选项强制生成线程堆栈信息:bash
代码解读复制代码jstack -F <PID>
异常没有发生定位异常代码,需要通过jmap
生成dump文件。
然后将其导入到 MAT 中进行分析。以下是生成堆转储文件的步骤:
jps
命令查看正在运行的 Java 进程及其 PID。jmap
命令生成堆转储文件。命令格式如下:shell 代码解读复制代码jmap -dump:file=<文件路径> <PID>
例如,要生成名为 heapdump.hprof
的堆转储文件,可以执行以下命令:shell
代码解读复制代码jmap -dump:file=heapdump.hprof <PID>
这将在当前工作目录下生成一个名为 heapdump.hprof
的堆转储文件。
File -> Open Heap Dump
,然后选择生成的堆转储文件。通过这些步骤可以手动生成堆转储文件并使用 MAT 进行分析,即使没有在 OutOfMemoryError 发生时自动生成堆转储文件也可以找到问题所在。
更适合线上环境的做法是在启动JVM时加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof
参数,这样当发生OutOfMemoryError时,JVM会自动生成堆转储文件。