不想为了监控拖垮生产
之前用 VisualVM 连生产抓采样,JMX 一开 CPU 就飘,GC 行为都变了,抓到的数据还不可信。后来发现 JDK 11 起的 JFR(Java Flight Recorder)开销极低,而且 JDK 14 起的 Event Streaming 能实时订阅事件,不用等 dump 文件。我们把它接进了监控管线,常开录制,出问题直接回放,再没为监控影响过业务。
JFR 事件类型
JFR 内置几百种事件,分两类:
- 持续型:如 jdk.CPUUsage、jdk.GCHeapSummary,周期性记录,采样频率可配;
- 点状型:如 jdk.ExceptionThrow、jdk.SocketRead,发生时才记,开销按需。
我们用得最多的是 jdk.GarbageCollection、jdk.ThreadPark、jdk.ObjectAllocationInNewTLAB,能看出 GC 节奏和锁竞争。默认 continuous profile 配置下,开销通常低于 1%,所以可以放心常开。
开启录制
# 启动即开,profiling 配置,磁盘保留 1 天
java -XX:StartFlightRecording=disk=true, \
filename=app.jfr, maxsize=200m, maxage=1d, \
settings=profile \
-jar app.jar
maxsize 防止磁盘写爆,maxage 控制保留时长。我们配了 200m 上限、保留 1 天,足够复盘最近的问题。
JFR Event Streaming 实时订阅
过去要等录完才能分析。JDK 14 起的 jdk.jfr.consumer 能在事件产生的同时读取。我们写了个常驻采集器,把异常事件实时推到 Prometheus,这样异常突增能立刻告警:
try (var es = EventStream.openRepository(Path.of("/tmp/jfr"))) {
es.onEvent("jdk.ExceptionThrow", e -> {
String ex = e.getClass("exceptionClass").getName();
exceptionCounter.labels(ex).inc();
});
es.onEvent("jdk.GarbageCollection", e ->
gcPause.observe(e.getDuration().toMillis()));
es.start();
}
也可以直连正在运行的 JVM:EventStream.openFlightRecorderStream(),无需落盘,事件一边产生一边流过来,延迟在毫秒级,适合做实时看板。
生产环境的真实开销
我们在 4 核 8G 的订单服务开 profile 级录制,QPS 5000 下 CPU 仅多 0.8 个百分点,GC 停顿没变化。对比之前 JMX 采样带来的 6% 抖动,JFR 几乎无感。一个月下来落盘 JFR 约 6GB,定期清理即可,还能在故障后用 jfr 命令回放分析,比如定位某次 Full GC 前到底谁在大量分配。
写在后面
现在回头看,《JFR 事件流与持续性能分析》本身不算多难,难的是线上真出问题那十分钟里的判断。经验都是这么来的。