1. JMX (Java Management Extensions):JVM 监控的心脏
监控就像对你的应用进行定期体检。日志可以比作日记,记录已经发生的一切;而监控是一组仪表,实时显示系统当前状态:温度、脉搏、血压、血糖以及其他对 JVM 至关重要的指标。
它帮助你了解实际使用了多少内存、是否存在泄漏、有多少线程在运行以及它们所处的状态——等待、运行还是被阻塞。你可以看到垃圾回收执行的频率、CPU 的负载,并能及时发现诸如内存或线程数突然增长等预警信号。
简而言之,日志讲述的是“已经发生了什么”,而监控展示的是“此时此刻正在发生什么”。
JMX 基础:是什么,有什么用
JMX 是 Java 的一项技术,允许你从内部观察应用的运行情况,甚至对其进行一定程度的管理。它通过特殊对象——MBean(Management Bean)来工作。可以把它理解为 JVM 内部的“传感器”和“开关”:有的用来显示系统状态,有的可以进行调节。
借助 JMX,你无需改动代码或重启应用,就能知道 JVM 当前分配了多少内存、有多少线程处于活动状态、垃圾回收耗时多少,以及更多信息。
其工作原理
在 JVM 中开箱即用地提供了大量 MBean,它们可以提供以下信息:
- 内存(heap、non-heap);
- 垃圾回收器(GC);
- 线程;
- 类(已加载与已卸载数量);
- 甚至是 JVM 本身(版本、启动参数)。
JMX 就像汽车的仪表盘。 你能看到速度、转速、发动机温度——这一切都通过标准的“传感器”(MBean)提供。
如何访问 JMX
最简单的方式是使用 JDK 自带的标准工具 JConsole。
启动 JConsole
- 打开终端/命令行。
- 执行命令:
jconsole - 选择你想监控的 Java 进程(例如你的应用)。
JConsole 将通过 JMX 连接到 JVM,并展示图表和表格:内存使用、线程数量、垃圾回收活动等。
示例:查看内存与线程
在 JConsole 中打开 "Memory" 与 "Threads" 选项卡。你会看到已用内存的变化、当前存活线程数、哪些线程处于活动状态、哪些在等待。
自定义 MBean
你可以创建自己的 MBean 来监控自定义指标(例如已处理订单的数量)。不过这是进阶内容:标准的 MBean 已经提供了非常多的信息。
2. VisualVM — 可视化监控与性能剖析
如果说 JConsole 是“仪表盘”,那么 VisualVM 就是一整个诊断中心,配有 X 光、核磁共振(MRI)和血液分析。它不仅能监控,还能对应用进行剖析,抓取 heap dump,分析内存泄漏,并查看哪些方法最耗时。
安装与启动 VisualVM
- VisualVM 随 JDK 提供(通常名为 jvisualvm),也可以在 visualvm.github.io 下载最新版本。
- 启动命令:
jvisualvm - 启动后你会看到本机上所有正在运行的 Java 进程列表。
连接到进程
- 在列表中找到你的进程(例如 Main 或 MyApp)。
- 双击它——会打开该进程的信息页。
- 完成!现在你可以看到:
- 实时的 CPU 与内存使用。
- 线程数量及其状态。
- 已加载类的列表。
- 抓取 heap dump 与 thread dump 的入口。
VisualVM 的主要功能
借助 VisualVM,你可以观察内存的使用情况——包括 heap 与 non-heap。图表能显示内存消耗是在增长还是趋于稳定。想要检查垃圾回收是否工作正常,点击 "Perform GC" 按钮——JVM 会立即尝试回收内存。你可以生成 heap dump(内存快照)并分析它来定位泄漏。
它也能展示线程状态:多少线程在运行、哪些在工作、哪些在等待或被阻塞。如果发生了 deadlock,VisualVM 能帮助检测并展示互相等待的关系。
对于更深入的分析,可以使用性能剖析:你能看到“热点”方法,即那些最耗费 CPU 时间或内存的方法。这对于诊断性能意外下降非常有用。
示例:监控一个小程序
public class MemoryLeakDemo {
public static void main(String[] args) throws InterruptedException {
List<byte[]> memoryConsumers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
memoryConsumers.add(new byte[1024 * 1024]); // 1 MB
Thread.sleep(100); // 稍微等待一下,让曲线更平滑
}
System.out.println("完成!别忘了在 VisualVM 里查看 :)");
Thread.sleep(60000); // 保持应用运行以便分析
}
}
现在:
- 运行程序。
- 打开 VisualVM,连接到该进程。
- 观察内存使用如何增长。
- 抓取 heap dump,找出占用内存的数组。
3. Java Flight Recorder (JFR):JVM 的“黑匣子”
什么是 JFR
Java Flight Recorder 是内置于 JVM 的事件采集工具。它是“黑匣子”:JFR 会记录 JVM 中发生的事情,方便之后定位瓶颈或事故原因。
JFR 会采集:
- GC 事件;
- 线程信息;
- 方法调用频率与时间剖析;
- 停顿与延迟;
- 异常与错误。
如何启用 JFR
从 Java 11 起,JFR 已内置在 OpenJDK 中。
使用 JFR 启动
java -XX:StartFlightRecording=filename=recording.jfr,duration=60s,settings=profile -jar MyApp.jar
- filename=recording.jfr — 保存文件的位置。
- duration=60s — 录制时长。
- settings=profile — 详细程度(default、profile、continuous)。
查看结果
用于分析 .jfr 文件的工具是 JDK Mission Control(JMC):
- 下载 JMC:jdk.java.net/jmc
- 打开文件 recording.jfr。
- 查看图表、热点方法、GC 停顿以及线程活动。
示例:录制“黑匣子”
- 使用 JFR 启动应用(见上面的命令)。
- 打开 JDK Mission Control,加载录制文件。
- 评估 CPU/内存热点、垃圾回收频率以及活跃线程数量。
4. 实践:监控一个应用
import java.util.ArrayList;
import java.util.List;
public class MonitoringExample {
public static void main(String[] args) throws InterruptedException {
List<byte[]> memory = new ArrayList<>();
for (int i = 0; i < 50; i++) {
memory.add(new byte[2 * 1024 * 1024]); // 2 MB
Thread.sleep(500);
}
// 启动一个线程
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
System.out.println("后台线程在运行...");
} catch (InterruptedException e) {
break;
}
}
}).start();
Thread.sleep(20000); // 保持应用运行以便监控
}
}
操作:
- 运行程序。
- 打开 VisualVM,找到该进程,查看内存图和线程。
- 尝试抓取 heap dump 与 thread dump。
- 进阶:使用 JFR 启动,并在 JMC 中查看结果。
5. 实用细节
监控工具对比表
| 工具 | 适用场景 | 如何启动 | 特点 |
|---|---|---|---|
| JConsole (JMX) | JVM 基本指标、线程、GC | |
简单,随 JDK 提供 |
| VisualVM | 监控、性能剖析、heap dump | |
图表、内存分析、CPU |
| Java Flight Recorder | JVM 事件的深度分析 | |
在 JMC 中分析,“黑匣子” |
| JDK Mission Control | 查看 JFR 文件 | 独立程序 | 详细分析与报告 |
建议
- 不仅在生产环境监控,也要在测试阶段监控。 这样可以更早发现内存泄漏和“挂起”的线程。
- Heap dump 并不可怕! 在怀疑内存泄漏时抓取内存转储,并在 VisualVM 中分析它。
- 大胆尝试使用 JFR。 即便一开始不太明白,事件与图表也能帮助定位瓶颈。
- 可以以编程方式使用 JMX。 例如将指标发送到 Prometheus/Grafana。
- 性能剖析不宜长期开启。 按需启用分析器,否则应用可能变慢。
6. JVM 监控中的常见错误
错误 1:完全不做监控。 许多新手认为只要应用“能跑”就一切正常。实际上,内存和线程问题常常在高负载或运行一段时间后才显现。别偷懒,至少定期打开 VisualVM/JConsole 看一看。
错误 2:在大型应用上未做准备就抓取 heap dump。 大型应用的 heap dump 可能有数 GB,并会让进程“冻结”数秒。尽量在测试环境或生产的低峰时段进行。
错误 3:忽视 GC 和线程指标。 如果 GC 占用的时间比例持续偏高——这是危险信号!如果线程数量稳定增长——请排查线程泄漏。
错误 4:不分析监控结果。 仅打开 VisualVM 还不够。看看哪些对象占据了内存,哪些方法“吃掉”了最多的 CPU,为何会出现 deadlock。分析堆栈跟踪与根因。
错误 5:在生产环境盲目使用性能分析器。 性能剖析可能显著拖慢应用。只在诊断时按需开启,而不是“长期运行”。
GO TO FULL VERSION