JMC 使用教程
# JMC 介绍
Java Mission Control(JMC)是一个强大的 Java 应用程序分析工具,从 2018 年开始,JMC 被 Oracle 开源作为独立工具发布。JMC 是一款可以用于管理、监听、分析以及排除 Java 故障的高级工具,使用 JMC 可以对代码性能、内存分配、GC 延迟、CPU 使用等情况进行低开销的、高效且详细的分析。
截止本文编写时间,JMC 最新版本8.3 .
开源地址:https://github.com/openjdk/jmc (opens new window)
Oracle 官方地址:https://www.oracle.com/java/technologies/jdk-mission-control.htmld
Oracle JMC 下载:https://www.oracle.com/java/technologies/javase/products-jmc8-downloads.html
# JMC 测试环境准备
下载安装 JMC 这里不做描述,根据自己的系统环境下载相应的 JMC 版本安装启动即可。有一点要注意的是,从 JMC 8.10 开始,所依赖的最低 JDK 版本为 JDK11,如果你是 JDK 8 环境,可以下载 JMC 8.0 版本。如果你所在的 JVM 环境为 32 位,那么应该使用 -Dcom.sun.management.jmxremote
参数来启动 JVM。
# 测试代码准备
在启动 JMC 之前,准备一段有很多问题的代码,这段代码的问题包括诸多问题。
文章测试代码地址:https://github.com/niumoo/JavaNotes/ (opens new window)
CPU 使用过高
通过不断地进行浮点运算来提高对 CPU 的消耗。
/** * 消耗CPU的线程 * 不断循环进行浮点运算 */ private static void cpuHigh() { Thread thread = new Thread(() -> { Thread.currentThread().setName("cpu_high_thread"); while (true){ double pi = 0; for (int i = 0; i < Integer.MAX_VALUE; i++) { pi += Math.pow(-1, i) / (2 * i + 1); } System.out.println("Pi: " + pi * 4); } }); thread.start(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17内存使用过高
不断地生成 BigDecimal 数字添加到 List 来增加对内存的使用。
/** * 不断新增 BigDecimal 信息到 list */ private static void allocate() { new Thread(()->{ Thread.currentThread().setName("memory_allocate_thread"); List<BigDecimal> list = new ArrayList<>(); for (int i = 0; i < Integer.MAX_VALUE; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } list.add(new BigDecimal(i)); } }).start(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17线程池堵塞
死锁
线程 dead_thread_A 与 线程 dead_thread_B 互相等待造成死锁。
/** * 死锁线程 * 线程 dead_thread_A 与 线程 dead_thread_B 互相锁死 */ private static void deadThread() { /** 创建资源 */ Object resourceA = new Object(); Object resourceB = new Object(); // 创建线程 Thread threadA = new Thread(() -> { Thread.currentThread().setName("dead_thread_A"); synchronized (resourceA) { System.out.println(Thread.currentThread() + " get ResourceA"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resourceB"); synchronized (resourceB) { System.out.println(Thread.currentThread() + " get resourceB"); } } }); Thread threadB = new Thread(() -> { Thread.currentThread().setName("dead_thread_A"); synchronized (resourceB) { System.out.println(Thread.currentThread() + " get ResourceB"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resourceA"); synchronized (resourceA) { System.out.println(Thread.currentThread() + " get resourceA"); } } }); threadA.start(); threadB.start(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# JMC 概览
启动测试代码后,启动 JMC 就可以在左侧的 JMV 浏览器中看到当前机器所有的 JVM 进程,选择测试代码进行通过 MBean 服务器进行连接,然后可以看到概览界面。
通过概览界面,可以看到进程的 JVM 内存使用情况,JVM CPU 使用情况,可以对进程的总体情况有一个初步的判断。
# JMC 触发器
JMC 触发器可以理解为一个 JVM 告警,可以对 JVM 进程的各种指标设置阈值,在超过指定阈值后可以收到告警提示,对进程异常情况监控十分方便。
这里对 CPU 占用率和死锁线程数进行监控,由于问题代码的原因,很快就触发了告警。
通过弹窗告警可以看到告警原因,以及对应的类信息,但是这里目前还看不到告警的具体原因,也就是说比如 CPU 占用率过高,并不知道为什么 CPU 会使用过高。
# JMC 分析内存
通过内存页面可以看到堆内存分配情况,由于问题代码中再不断地分配 BigDecimal 对象,这里也可以看出 BigDecimal 对象占用了最多的内存。对分析内存过高情况有一定的帮助。
如果想进一步分析内存占用来源,可以到线程页面,通过勾选分配复选框,可以看到哪个线程占用的内存最多,还可以看到线程的具体的调用堆栈。
# JMC 分析CPU
线程页面勾选 CPU 概要分析可以查看占用 CPU 最高的线程,这里线程 cpu_high_thread
在不断地进行浮点计算,占用了较多的 CPU。
# JMC 分析死锁
线程页面勾选死锁检测可以直接看到死锁线程信息,并且有具体的线程堆栈。
另外一种检查死锁的方式是直接打印线程信息,在线程信息的最后部分,会输出死锁线程信息。使用 JMC 可以通过诊断命令中的 Thread.print
命令来实现这个功能。
一如既往,文章测试代码地址:https://github.com/niumoo/JavaNotes/ (opens new window)
提示:评论前请刷新页面,否则评论的可能不是当前文章。