Java内存分析相关工具
1、jps 工具(jdk自带,列出java进程)
jps(Java Virtual Machine Process Status Tool)是JDK提供的一个可以列出正在运行的Java虚拟机的进程信息的命令行工具,它可以显示 Java 虚拟机进程的执行主类(Main Class,main()函数所在的类)名称、本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)等信息。另外,jps 命令只能显示它有访问权限的Java进程的信息。
命令语法:
jps [-q] [-mlvV] [hostid]
jps [-help]
命令参数说明:
- -q:不显示主类名称、JAR文件名和传递给主方法的参数,只显示本地虚拟机唯一ID。
-mlvV:我们可以指定这些参数的任意组合。
- -m:显示Java虚拟机启动时传递给main()方法的参数。
- -l:显示主类的完整包名,如果进程执行的是JAR文件,也会显示JAR文件的完整路径。
- -v:显示Java虚拟机启动时传递的JVM参数。
- -V:不显示主类名称、JAR文件名和传递给主方法的参数,只显示本地虚拟机唯一ID。
- hostid:指定的远程主机,可以是ip地址和域名, 也可以指定具体协议,端口。如果不指定,则显示本机的Java虚拟机的进程信息。
- -help:显示jps命令的帮助信息。
2、jstack(jdk自带,打印线程当前堆栈快照)
jstack 是 JVM 自带的 Java 堆栈跟踪工具,它用于打印出给定的 java 进程ID、core file、远程调试服务的Java堆栈信息。jstack 命令用于生成虚拟机当前时刻的线程快照。
命令格式:
jstack [ option ] pid // 打印某个进程的堆栈信息 jstack [ option ] executable core jstack [ option ] [server-id@]remote-hostname-or-IP
3、jmap 工具(jdk自带,查看堆内存快照)
jmap 命令是 JDK 提供的一个命令行工具,用于生成Java虚拟机的堆转储快照(dump文件),也被称为 heapdump 或 dump 文件。这个命令不仅可以获取dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当时使用的是哪种垃圾收集器等。
命令语法:
jmap [options] pid
pid
:进程ID。option
:jmap命令的可选参数。如果没有指定该参数,jinfo命令会显示Java虚拟机进程的内存映像信息
下图示例:
3.1、jmap 常见参数
以下是 jmap 常见的参数和其作用的简要讲解:
-heap
:显示 Java 堆的配置和使用情况。这个选项会输出 Java 堆的配置信息,包括堆的大小、各代的大小和使用情况。-histo
:显示 Java 堆中各个类的实例数量和占用内存。这个选项会输出 Java 堆中各个类的实例数量和占用内存,按照内存大小降序排列。-dump:live,format=b,file=<filename>
:生成堆转储文件。这个选项用于生成 Java 堆的转储文件。live
表示只转储活动对象,format=b
表示使用二进制格式,file=<filename>
指定输出文件名。-F
:当进程不响应时,强制生成堆转储文件。jmap -F -dump:format=b,file=heap_dump.bin <pid>
-F
选项用于在 Java 进程不响应时强制生成堆转储文件。- -hprof[:]:以 HPROF 格式生成堆转储文件,可通过端口进行远程连接。
jmap -hprof:port=<port> <pid>
这个选项以HPROF
格式生成堆转储文件,并可以通过指定的端口进行远程连接。 -finalizerinfo
:显示等待终结的对象的信息。这个选项显示等待终结的对象的信息,有助于识别终结线程是否正常工作。-permstat
:显示永久代的详细信息。这个选项用于显示永久代的详细信息,包括类加载器、类元数据等信息。
以上是一些常见的 jmap
参数,通过这些参数可以获取关于 Java 进程内存使用情况的详细信息,用于诊断和解决内存相关的性能问题。
3.1.1、jmap -dump:format=b,file=path(将JVM的heap内容输出到文件中)
jmap -dump:format=b,file=path PID # 示例 jmap -dump:format=b,file=/usr/msg/20240203_heapdump.hprof 10016 jmap -dump:format=b,file=F:/myHeapdumpTest/20240203_heapdump.hprof 10016
通过以上命令可以将JVM的heap内容输出到文件中,生成堆转储快照,这对于分析内存使用情况和定位内存泄漏问题非常有用。
示例:
注意,通过以上命令获取 dump 文件必须是一出现内存异常就获取 dump 文件,这样获取的文件信息才比较准确。如果无法及时获取,可能内存已经被释放,获取到的信息对于分析问题意义就不大了。
3.1.2、jmap -heap pid(显示 Java 堆的配置和使用情况)
显示Java堆的如下信息:
- 被指定的垃圾回收算法的信息,包括垃圾回收算法的名称和垃圾回收算法的详细信息。
- 堆的配置信息,可能是由命令行选项指定,或者由Java虚拟机根据服务器配置选择的。
- 堆的内存空间使用信息,包括分代情况,每个代的总容量、已使用内存、可使用内存。如果某一代被继续细分(例如,年轻代),则包含细分的空间的内存使用信息。
代码演示,通过以下步骤演示使用 jmap 分析代码执行过程中的堆内存。
package cn.itcast.jvm.t1.heap; /** * 演示堆内存 */ public class Demo1_4 { public static void main(String[] args) throws InterruptedException { System.out.println("1..."); Thread.sleep(30000); byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb System.out.println("2..."); Thread.sleep(20000); array = null; System.gc(); System.out.println("3..."); Thread.sleep(1000000L); } }
以上代码将依次输出1、2、3,如下:
我们将依次在程序输出1、2、3时打印出此时的堆内存。
如下,在输出 1 时打印的堆内存,可以看到此时程序刚开始启动,堆内存占用并不大。
在输出 2 时打印堆内存如下,因为此时已经创建了一个 10m 大小的字节数组对象,所以可以看到堆内存增加了差不多 10m 的大小。
在输出 3 时打印堆内存如下,因为此时我们没有代码指向那个字节数组对象,并且主动调用了垃圾回收,所以可以看到堆内存减少了差不多 10m 的大小,这是因为那个字节数组对象被垃圾回收掉了。
4、jconsole(jdk自带,监控内存CPU等指标)
Jconsole 是 jdk 自带的一套java虚拟机执行状况监视器,它能够用来监控虚拟机的内存,线程,cpu使用情况以及相关的java进程相关的MBean。jconsole 跟 jmap 不一样,jconsole 可以一直监控内存等情况,而 jmap 只是打印出当前时刻的堆内存情况,相对来说 jconsole 更好用些。
如果有安装 jdk 的话,直接在命令中输入 jconsole 即可弹出 jconsole 窗口,然后输入连接 ip 等配置即可。主界面示例以下:
演示,假如我们执行以下代码:
package cn.itcast.jvm.t1.heap; /** * 演示堆内存 */ public class Demo1_4 { public static void main(String[] args) throws InterruptedException { System.out.println("1..."); Thread.sleep(30000); byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb System.out.println("2..."); Thread.sleep(20000); array = null; System.gc(); System.out.println("3..."); Thread.sleep(1000000L); } }
在启动 main 方法后,在命令行中直接输入 jconsole,可以看到堆内存的趋势,如下:
可以看到,堆内存在一个时间点急剧上升,这是因为我们创建了一个接近 10m 的字节数组对象,然后又在一个时间点急剧下降,这是因为此时那个字节数组对象已经没有引用,并且我们主动调用了垃圾回收,所以可以看到堆内存减少了差不多 10m 的大小,。
启动 jconsole 时,可以选择本地连接,然后选择对应的程序,如下,连接后提示不安全的连接直接点击确定即可。
5、jvisualvm(jdk自带,性能分析工具)
jvisualvm 是 JDK 自带的 JAVA 性能分析工具,它默认已经在你的JDK bin目录里了,只要你使用的是JDK1.6 Update7之后的版本,点击一下jvisualvm.exe图标它就可以运行了,如 E:\Develop\jdk1.8.0_202\bin\jvisualvm.exe 。
如演示分析以下代码执行垃圾回收后,内存占用仍然很高
package cn.itcast.jvm.t1.heap; import java.util.ArrayList; import java.util.List; /** * 演示查看对象个数 堆转储 dump */ public class Demo1_13 { public static void main(String[] args) throws InterruptedException { List<Student> students = new ArrayList<>(); for (int i = 0; i < 200; i++) { students.add(new Student()); // Student student = new Student(); } Thread.sleep(1000000000L); } } class Student { private byte[] big = new byte[1024*1024]; }
启动上面 java 程序后,在命令行输入 jvisualvm 打开分析工具,如下:
6、MAT 内存分析工具
6.1、MAT工具的介绍
想要深入的进行分析并确定内存泄漏,就要分析疑似发生内存泄漏时所生成堆存储文件(hprof)。堆存储文件可以使用DDMS或者Memory Monitor来生成,输出的文件格式为hpof,而MAT就是来分析堆存储文件的。
MAT(Memory Analyzer Tool)工具是一款功能强大的]ava堆内存分析器。可以用于查找内存泄漏以及查看内存消耗情况。MAT是基于Eclipse开发的,不仅可以单独使用,还可以作为插件的形式嵌入在Eclipse中使用。
下载地址 : https://www.eclipse.org/mat/downloads.php(当使用 MAT 工具报错 jdk 版本过低时,此时可以参考:https://blog.csdn.net/health7788/article/details/123893540 指定该工具使用其他版本的 jdk。)
(注意,默认情况下,MAT 工具只能识别Oracle JDK
的 dump 格式日志,而对于IBM JDK
的.phd
格式的日志,需要额外安装插件DTFJ
)
6.2、MAT工具的基本使用
用mat打开hprof文件后会看到的是一个饼状图,它主要用来显示内存的消耗,饼状图的彩色区域代表被分配的内存,灰色区域的则是空闲内存,点击每个彩色区域可以看到这块区域的详细信息,
6.3、MAT工具的视图
MAT 几个比较重要的视图:Leak Suspects(泄露疑点)、histogram(直方图视图,列出每个类的实例数)、dominator tree(支配树视图,直接列出最大的类)
6.3.1、Leak Suspects(泄露疑点,可查看内存泄露的线程以及详细堆栈信息)
打开怀疑泄露报告可能会看到有多个怀疑对象(Problem Suspect),可以从占用内存从大到小开始分析(报告里面占用内存都是用的字节单位,1,000,000,000 bytes = 1GB)
Leak Suspects 有的时候甚至连具体代码的位置都帮我们定位好了,排查非常方便。
在有些比较简单的问题里直接查看堆栈即可看到内存溢出原因,甚至可定位到具体代码行,如下图。当实际情况往往不会这么容易就能排查出问题,需要结合树状图等其他视图内容查看。
6.3.2、dominator tree(树状图,可排序查看占用内存大的类)
- Retained Heap:类所占用的内存大小,以字节为单位
- Percentage:表示占用 JVM 内存的百分比
下图中可看出有一个占用 99.42% 内存的对象,里面包含了一个字符串对象,该对象即是导致内存溢出的问题。
有时可能不是一个特别大的对象占满了内存,而是有很多个小对象占满内存,所以也要看下有没有虽然排序在后面但是有很多个重复的相同对象的情况。
6.3.3、直方图视图(histogram)
查看对象占用的内存大小
可查看大对象堆内存占用情况的一个列表,列说明如下:
- Shallow Heap:大对象占用的堆内存大小
- Retained Heap:与这个大对象关联的堆内存大小
7、IBM Heap Analyzer