怎么用JDK自带工具进行JVM内存分析

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: JVM内存分析工具,如`jps`、`jcmd`、`jstat`、`jstack`和`jmap`,是诊断和优化Java应用的关键工具。`jps`列出Java进程,`jcmd`执行诊断任务,如查看JVM参数和线程堆栈,`jstat`监控内存和GC,`jstack`生成线程堆栈信息,而`jmap`则用于生成堆转储文件。这些工具帮助排查内存泄漏、优化内存配置、性能调优和异常分析。例如,`jmap -dump:file=heapdump.hprof <PID>`生成堆转储文件,之后可以用Eclipse Memory Analyzer (MAT)等工具分析。

进行jvm内存分析可以排查存在和潜在的问题。

通过借助jdk自带的常用工具,可以分析大概可能的问题定位以及确定优化方向。

JVM内存分析有很多好处。

  • 内存泄漏排查:JVM 内存泄漏是指应用程序中的对象占用的内存无法被垃圾回收器释放,导致内存占用持续增长,最终耗尽可用内存。通过内存分析工具,可以检测到哪些对象占用了大量内存且无法被释放,进而定位到可能存在内存泄漏的代码。
  • 内存优化:合理优化 JVM 内存配置可以提高应用程序的性能和稳定性。通过分析应用程序的内存使用情况,可以调整堆内存大小、永久代(如果是旧版 Java)大小、新生代与老年代比例等参数,以减少垃圾回收频率,降低内存占用。
  • 性能调优:内存分析也有助于发现内存中的瓶颈,如频繁的 Full GC(全局垃圾回收)导致的停顿时间过长。通过调整垃圾回收器类型、GC 算法、堆内存大小等参数,可以改善应用程序的性能表现。
  • 异常分析:当应用程序出现内存相关的异常,如 OutOfMemoryError(内存溢出错误)时,通过分析内存使用情况可以找到导致异常的根本原因,例如某个模块或对象占用了过多内存。
  • 容量规划:对于大型应用程序或者需要长时间运行的系统,进行内存分析可以帮助进行容量规划,确保系统具有足够的内存资源支持应用程序的正常运行。

本文将通过一次jvm内存分析过程来说明jpsjcmdjstatjstackjmap 工具的使用方法。

本文使用到的是JDK17版本。

一次jvm内存分析之旅

当需要进行 JVM 内存分析时,结合使用 jpsjcmdjstatjstackjmap 可以提供全面的诊断信息。

下面是一般的步骤:

使用 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

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: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 命令包括:

  • help: 显示 jcmd 支持的命令列表,以及每个命令的简要描述。
  • VM.version: 显示 JVM 的版本信息。
  • VM.flags: 显示 JVM 的启动参数。
  • VM.system_properties: 显示 JVM 的系统属性。
  • Thread.print: 打印 Java 进程中所有线程的堆栈信息。
  • GC.run: 执行一次垃圾回收。
  • GC.heap_dump: 生成 Java 堆转储文件(heap dump)。

举例来说,如果要打印指定 Java 进程的线程堆栈信息,可以使用以下命令:

bash

代码解读

复制代码

jcmd <PID> Thread.print

如果要执行一次垃圾回收,可以使用以下命令:

bash

代码解读

复制代码

jcmd <PID> GC.run

如果要生成 Java 堆转储文件,可以使用以下命令:

bash

代码解读

复制代码

jcmd <PID> GC.heap_dump <filename>

jstat

jstat:jstat 命令可以监视 JVM 内存、垃圾回收等情况。例如,可以使用 jstat -gc <pid> 查看垃圾回收统计信息,或者使用 jstat -gcutil <pid> 查看垃圾回收统计信息及内存使用情况。

下面是 jstat 的基本使用方法:

bash

代码解读

复制代码

jstat [ options ] <PID> [ interval [ count ] ]

其中:

  • [ options ]:可选的命令选项,用于指定要监视的数据类型和格式。
  • <PID>:要监视的 Java 进程的 PID(进程标识符)。
  • [ interval ]:可选参数,指定输出统计信息的时间间隔(以毫秒为单位)。如果省略,则默认为每秒一次。
  • [ count ]:可选参数,指定输出统计信息的次数。如果省略,则默认为无限次输出。

常用的 jstat 命令选项包括:

  • -class: 显示类加载、卸载信息以及类加载器的状态。
  • -gc: 显示垃圾回收相关的信息,包括各个代的使用情况、GC 时间等。
  • -compiler: 显示即时编译器(JIT)的编译统计信息。
  • -gccapacity: 显示各个堆区的容量及使用情况。
  • -gcutil: 显示各个堆区的使用情况,以百分比表示。
  • -gccause: 显示导致最近一次 GC 的原因。
  • -printcompilation: 打印方法的即时编译(JIT)信息。

举例来说,要查看 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:jstack 命令用于生成 Java 线程转储快照,可以用于分析线程状态、死锁等问题。例如,可以使用 jstack <pid> 打印线程堆栈信息,或者使用 jstack -l <pid> 打印线程堆栈信息及锁信息。

下面是 jstack 命令的基本使用方法:

bash

代码解读

复制代码

jstack [ options ] <PID>

其中:

  • [ options ]:可选的命令选项,用于指定输出的格式等。
  • <PID>:要生成线程堆栈信息的 Java 进程的 PID(进程标识符)。

常用的 jstack 命令选项包括:

  • -l: 长列表格式,显示关于锁的附加信息,如拥有者和等待队列。
  • -F: 当正常输出的 jstack 命令不起作用时,强制生成线程堆栈信息。这在 Java 进程没有响应时可能会很有用,但可能会导致进程暂停一段时间。
  • -m: 显示 Java 和本地方法的堆栈跟踪,而不仅仅是 Java 堆栈跟踪。
  • -h: 显示帮助信息。

举例来说,要生成指定 Java 进程的线程堆栈信息,可以使用以下命令:

bash

代码解读

复制代码

jstack <PID>

如果想要输出长列表格式的线程堆栈信息,可以使用 -l 选项:

bash

代码解读

复制代码

jstack -l <PID>

如果 Java 进程没有响应,可以使用 -F 选项强制生成线程堆栈信息:

bash

代码解读

复制代码

jstack -F <PID>

jmap

异常没有发生定位异常代码,需要通过jmap生成dump文件。

然后将其导入到 MAT 中进行分析。以下是生成堆转储文件的步骤:

  • 确定 Java 进程 ID:首先,需要确定正在运行的 Java 进程的进程 ID(PID)。可以使用 jps 命令查看正在运行的 Java 进程及其 PID。
  • 生成堆转储文件:使用 jmap 命令生成堆转储文件。命令格式如下:

shell

代码解读

复制代码

jmap -dump:file=<文件路径> <PID>

例如,要生成名为 heapdump.hprof 的堆转储文件,可以执行以下命令:

shell

代码解读

复制代码

jmap -dump:file=heapdump.hprof <PID>

这将在当前工作目录下生成一个名为 heapdump.hprof 的堆转储文件。

  • 导入堆转储文件到 MAT:将生成的堆转储文件导入到 MAT 中进行分析。打开 MAT,然后选择 File -> Open Heap Dump,然后选择生成的堆转储文件。
  • 执行内存分析:一旦堆转储文件被导入到 MAT 中,就可以执行内存分析,按照前面提到的步骤来查找内存问题。

通过这些步骤可以手动生成堆转储文件并使用 MAT 进行分析,即使没有在 OutOfMemoryError 发生时自动生成堆转储文件也可以找到问题所在。

更适合线上环境的做法是在启动JVM时加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof参数,这样当发生OutOfMemoryError时,JVM会自动生成堆转储文件。


转载来源:https://juejin.cn/post/7359374598083641354

相关文章
|
15天前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
36 2
|
11天前
|
存储 算法 Java
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
本文介绍了 JVM 的内存区域划分、类加载过程及垃圾回收机制。内存区域包括程序计数器、堆、栈和元数据区,每个区域存储不同类型的数据。类加载过程涉及加载、验证、准备、解析和初始化五个步骤。垃圾回收机制主要在堆内存进行,通过可达性分析识别垃圾对象,并采用标记-清除、复制和标记-整理等算法进行回收。此外,还介绍了 CMS 和 G1 等垃圾回收器的特点。
45 0
深入解析 Java 虚拟机:内存区域、类加载与垃圾回收机制
|
16天前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
25 1
|
18天前
|
存储 Prometheus NoSQL
Redis 内存突增时,如何定量分析其内存使用情况
【9月更文挑战第21天】当Redis内存突增时,可采用多种方法分析内存使用情况:1)使用`INFO memory`命令查看详细内存信息;2)借助`redis-cli --bigkeys`和RMA工具定位大键;3)利用Prometheus和Grafana监控内存变化;4)优化数据类型和存储结构;5)检查并调整内存碎片率。通过这些方法,可有效定位并解决内存问题,保障Redis稳定运行。
|
1月前
|
监控 IDE Java
【Java性能调优新工具】JDK 22性能分析器:深度剖析,优化无死角!
【9月更文挑战第9天】JDK 22中的性能分析器为Java应用的性能调优提供了强大的支持。通过深度集成、全面监控、精细化分析和灵活报告生成等核心优势,性能分析器帮助开发者实现了对应用性能的全面掌控和深度优化。在未来的Java开发过程中,我们期待性能分析器能够继续发挥重要作用,为Java应用的性能提升贡献更多力量。
|
1月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
1月前
|
NoSQL 程序员 Linux
轻踩一下就崩溃吗——踩内存案例分析
踩内存问题分析成本较高,尤其是低概率问题困难更大。本文详细分析并还原了两个由于动态库全局符号介入机制(it's a feature, not a bug)触发的踩内存案例。
|
2月前
|
Python
Python变量的作用域_参数类型_传递过程内存分析
理解Python中的变量作用域、参数类型和参数传递过程,对于编写高效和健壮的代码至关重要。正确的应用这些概念,有助于避免程序中的错误和内存泄漏。通过实践和经验积累,可以更好地理解Python的内存模型,并编写出更优质的代码。
21 2
|
2月前
|
NoSQL Java 测试技术
Golang内存分析工具gctrace和pprof实战
文章详细介绍了Golang的两个内存分析工具gctrace和pprof的使用方法,通过实例分析展示了如何通过gctrace跟踪GC的不同阶段耗时与内存量对比,以及如何使用pprof进行内存分析和调优。
57 0
Golang内存分析工具gctrace和pprof实战
|
1月前
使用qemu来dump虚拟机的内存,然后用crash来分析
使用qemu来dump虚拟机的内存,然后用crash来分析