文章 601
评论 5
浏览 235819
Arthas 之外还有这些 Java 诊断利器,第 3 个绝了

Arthas 之外还有这些 Java 诊断利器,第 3 个绝了

一、引言

"线上 CPU 突然飙到 100%,你第一反应是什么?"

对于大多数 Java 开发者来说,答案可能是:"登录服务器,启动 Arthas!"

Arthas 确实是 Java 诊断领域的瑞士军刀,但它不是万能的。今天,我要给你介绍几款更专业、更强大的诊断工具,尤其是第 3 个——async-profiler,堪称"核弹级"推荐。


二、诊断工具全景图

在深入每个工具之前,先来看一张全景对比图:

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│                    Java 诊断工具对比                            │
│                                                                 │
│  ┌──────────────┬──────────┬──────────┬──────────┬───────────┐ │
│  │ 工具         │ 是否需登录│ 性能影响 │ 功能覆盖 │ 学习成本  │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ Arthas       │ ✅ 需要   │ 低-中    │ 全能     │ 低        │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ jconsole     │ ❌ 远程   │ 低       │ 基础     │ 极低      │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ jvisualvm    │ ❌ 远程   │ 低-中    │ 进阶     │ 低        │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ async-profiler│ ✅ 需要   │ 极低     │ 性能分析 │ 中        │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ BTrace       │ ✅ 需要   │ 低       │ 动态追踪 │ 高        │ │
│  ├──────────────┼──────────┼──────────┼──────────┼───────────┤ │
│  │ JFR          │ ✅/❌     │ 零-极低  │ 全链路   │ 中        │ │
│  └──────────────┴──────────┴──────────┴──────────┴───────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

三、工具一:Arthas——经典全能选手

3.1 为什么 Arthas 是首选?

Arthas 是阿里巴巴开源的 Java 诊断工具,几乎能解决你遇到的所有问题:

# 常用命令一览
$ arthas-boot

# 1. 查看线程状态
thread -n 3

# 2. 监控方法执行
watch com.example.service.UserService getUser "{params, returnObj}"

# 3. 反编译类
jad com.example.controller.OrderController

# 4. 查看类加载器
classloader -t

# 5. 性能分析
profiler start
profiler status
profiler stop -f flame.html

3.2 Arthas 的局限性

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  Arthas 的局限性                                             │
│                                                             │
│  ❌ 需要登录服务器                                           │
│     - 安全审计要求严格的环境无法使用                          │
│     - 需要 sudo 权限                                         │
│                                                             │
│  ❌ Profiler 准确度一般                                      │
│     - 使用的是 Java 层采样                                   │
│     - 对于 native 方法和系统调用不够准确                      │
│                                                             │
│  ❌ 对 JVM 有一定侵入性                                      │
│     - 通过 attach 机制注入 agent                             │
│     - 极端情况下可能影响应用性能                              │
│                                                             │
│  ❌ 无法生成高质量火焰图                                      │
│     - 生成的火焰图信息不够完整                                │
│     - 没有 async-profiler 专业                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

四、工具二:jconsole/jvisualvm——JDK 自带的基础工具

4.1 jconsole——轻量级监控

# 启动方式
$ jconsole

# 连接方式:
# 1. 本地连接(同一台机器)
# 2. 远程连接(需要配置 JMX)

远程 JMX 配置

java -jar myapp.jar \
  -Dcom.sun.management.jmxremote \
  -Dcom.sun.management.jmxremote.port=9999 \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Djava.rmi.server.hostname=192.168.1.100

主要功能

  • 内存使用情况监控
  • 线程状态查看
  • CPU 使用率图表
  • 类加载统计

4.2 jvisualvm——更强大的可视化工具

# 启动方式
$ jvisualvm

# 插件安装:
# 1. 菜单栏 -> 工具 -> 插件
# 2. 可用插件 -> 选择需要的插件 -> 安装

关键插件

  • Visual GC:可视化垃圾回收
  • MBeans:查看和操作 MBean
  • Sampler:CPU 和内存采样分析

4.3 优缺点总结

优点:
├── 零依赖(JDK 自带)
├── 远程连接无需登录服务器
├── 界面友好,易于上手
└── 基础功能足够日常排查

缺点:
├── 功能有限,深度分析不够
├── 远程连接需要配置 JMX(安全风险)
├── 没有火焰图生成能力
└── 对于复杂问题力不从心

五、工具三:async-profiler——核弹级性能分析神器 ⭐⭐⭐

5.1 为什么说它是"核弹级"?

async-profiler 是一款基于 HotSpot JVM 的高性能采样分析器,特点:

  • 四种采样模式:CPU、内存分配、锁、墙钟时间
  • 极低性能影响:采样模式下几乎不影响应用性能
  • 高精度:基于 native 层采样,准确度远超 Java 层工具
  • 火焰图生成:一键生成专业级火焰图

5.2 安装与使用

# 安装
$ git clone https://github.com/jvm-profiling-tools/async-profiler
$ cd async-profiler
$ make

# 基本用法
$ ./profiler.sh -d 30 -f flame.html <pid>

# 查看帮助
$ ./profiler.sh --help

5.3 四种采样模式详解

模式一:CPU 采样(默认)

# CPU 采样 30 秒,生成火焰图
./profiler.sh -d 30 -f cpu-flame.html <pid>

# 带方法过滤的 CPU 采样
./profiler.sh -d 30 -f cpu-filtered.html -i "com.example.**" <pid>

适用场景:CPU 使用率高、响应慢、线程阻塞

模式二:内存分配采样

# 内存分配采样,统计分配热点
./profiler.sh -d 30 -e alloc -f alloc-flame.html <pid>

# 指定采样间隔(每分配 512KB 采样一次)
./profiler.sh -d 30 -e alloc -i 512k -f alloc-flame.html <pid>

适用场景:内存占用高、频繁 GC、OOM 排查

模式三:锁竞争采样

# 锁竞争采样,找出锁等待热点
./profiler.sh -d 30 -e lock -f lock-flame.html <pid>

# 分析特定锁
./profiler.sh -d 30 -e lock -f lock-specific.html --lock com.example.MyLock <pid>

适用场景:锁竞争严重、线程阻塞、响应延迟

模式四:墙钟时间采样

# 墙钟时间采样,包含等待时间(如 IO 等待)
./profiler.sh -d 30 -e wall -f wall-flame.html <pid>

# 分析特定线程
./profiler.sh -d 30 -e wall -f wall-thread.html -t <pid>

适用场景:总响应时间长、IO 密集型应用、慢查询分析

5.4 火焰图解读

火焰图解读技巧:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  火焰图 = 堆栈可视化                                          │
│                                                             │
│  X 轴:采样次数(宽度 = 时间占比)                            │
│  Y 轴:调用栈深度(从下往上)                                  │
│  颜色:随机颜色,无特殊含义                                    │
│                                                             │
│  读火焰图:                                                   │
│  1. 找"平顶"——宽度很大的函数                                 │
│  2. 看"深度"——调用链越长越值得关注                            │
│  3. 关注"热点路径"——从底部到顶部的完整路径                     │
│                                                             │
│  示例:                                                       │
│  ┌─────────────────────────────────────────────────────┐     │
│  │   MyService.process()  ████████████████████████████ │     │
│  │     └─ UserDAO.query()  ████████████████████       │     │
│  │       └─ JDBC.execute()  ██████████████           │     │
│  │         └─ MySQL.query()  ████████                 │     │
│  └─────────────────────────────────────────────────────┘     │
│                                                             │
│  结论:MyService.process() 是热点,大部分时间耗在 DB 查询        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.5 与 Arthas Profiler 的对比

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  async-profiler vs Arthas Profiler                          │
│                                                             │
│  对比维度         async-profiler         Arthas Profiler     │
│  ──────────────   ───────────────────    ─────────────────   │
│  采样精度         Native 层采样          Java 层采样          │
│  准确度           极高                    中等                 │
│  性能影响         <1%                     5%-10%              │
│  火焰图质量       专业级                   基础级               │
│  支持模式         CPU/alloc/lock/wall     CPU                  │
│  易用性           中等                     低                   │
│  学习成本         中等                     低                   │
│                                                             │
│  结论:                                                       │
│  ├── 日常快速排查:Arthas 更方便                              │
│  ├── 深度性能分析:async-profiler 是首选                      │
│  └── 生产环境调优:必须掌握 async-profiler                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

六、工具四:BTrace——安全的生产环境动态追踪

6.1 什么是 BTrace?

BTrace 是一个安全的 Java 动态追踪工具,可以在不修改代码、不重启应用的情况下,对生产环境进行实时追踪。

核心特点

  • 安全沙箱:不允许修改程序状态
  • 只读追踪:只能观察,不能干预
  • 热部署:脚本可以动态加载
  • 低侵入性:基于 Java Agent,影响极小

6.2 BTrace 脚本示例

示例 1:监控方法调用耗时

@BTrace
public class MethodDuration {
    
    @TLS
    static long startTime;
    
    @OnMethod(clazz = "com.example.service.OrderService", 
              method = "createOrder")
    public static void onEnter() {
        startTime = System.currentTimeMillis();
    }
    
    @OnMethod(clazz = "com.example.service.OrderService", 
              method = "createOrder",
              location = @Location(Kind.RETURN))
    public static void onReturn() {
        long duration = System.currentTimeMillis() - startTime;
        if (duration > 1000) {
            println("Slow method: createOrder, duration: " + duration + "ms");
            jstack();
        }
    }
}

示例 2:监控异常抛出

@BTrace
public class ExceptionMonitor {
    
    @OnMethod(clazz = "java.lang.Throwable", 
              method = "<init>")
    public static void onException(Throwable th) {
        println("Exception: " + th.getClass().getName());
        println("Message: " + th.getMessage());
        jstack();
    }
}

6.3 运行 BTrace

# 编译脚本
btracec MethodDuration.java

# 运行追踪
btrace <pid> MethodDuration.java

# 查看已加载的脚本
btrace -l <pid>

# 卸载脚本
btrace -u <pid> <script-id>

6.4 适用场景

适用场景:
├── 监控方法调用耗时
├── 追踪异常抛出
├── 统计方法调用次数
├── 监控参数和返回值
├── 分析线程等待时间

不适用场景:
├── 修改程序状态
├── 性能分析(不如 async-profiler)
├── 内存分析

七、工具五:JFR——零性能损耗的生产级事件记录

7.1 什么是 JFR?

JDK Flight Recorder(JFR) 是 Java 内置的事件记录系统,从 Java 11 开始开源,具有以下特点:

  • 零性能损耗:默认开启的情况下性能影响 <1%
  • 全链路追踪:从 JVM 到应用层的完整事件记录
  • 生产级安全:专为生产环境设计
  • 丰富的事件类型:GC、线程、锁、IO、网络等

7.2 三种启动方式

方式一:启动时开启

java -jar myapp.jar \
  -XX:StartFlightRecording=duration=60s,filename=profile.jfr

方式二:运行时开启(使用 jcmd)

# 查看 JVM 进程
jps

# 开始记录(60 秒)
jcmd <pid> JFR.start duration=60s filename=profile.jfr

# 查看记录状态
jcmd <pid> JFR.check

# 停止记录
jcmd <pid> JFR.stop

方式三:环境变量开启

export JAVA_TOOL_OPTIONS="-XX:+FlightRecorder"
java -jar myapp.jar

7.3 JFR 事件类型

JFR 事件类型:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  1. GC 事件                                                  │
│     ├── GC Pause                                             │
│     ├── GC Phase Summary                                     │
│     ├── Heap Statistics                                      │
│     └── Allocation Failure                                   │
│                                                             │
│  2. 线程事件                                                  │
│     ├── Thread Start/Stop                                    │
│     ├── Thread Dump                                          │
│     ├── Lock Contention                                      │
│     └── Thread Park                                          │
│                                                             │
│  3. IO 事件                                                   │
│     ├── File Read/Write                                      │
│     ├── Socket Read/Write                                    │
│     └── Process IO                                           │
│                                                             │
│  4. 应用事件                                                  │
│     ├── Method Profiling                                     │
│     ├── Exception Thrown                                     │
│     ├── Timer Event                                          │
│     └── Custom Event                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.4 使用 JMC(JDK Mission Control)分析

# 启动 JMC
$ jmc

# 打开 JFR 文件:
# 1. 文件 -> 打开 -> 选择 .jfr 文件
# 2. 查看各维度数据

JMC 分析维度

  • 概览:CPU、内存、GC、线程等核心指标
  • 内存:堆使用、分配速率、GC 暂停
  • 代码:方法热点、调用树、异常统计
  • 线程:线程状态、锁竞争、死锁检测
  • IO:文件、网络、进程 IO

7.5 自定义 JFR 事件

@Name("com.example.OrderCreateEvent")
@Label("Order Create Event")
@Category("Business")
public class OrderCreateEvent extends Event {
    
    @Label("Order ID")
    public long orderId;
    
    @Label("User ID")
    public long userId;
    
    @Label("Amount")
    public double amount;
    
    @Label("Duration")
    public long duration;
}

// 使用自定义事件
public class OrderService {
    
    public Order createOrder(OrderRequest request) {
        OrderCreateEvent event = new OrderCreateEvent();
        event.orderId = generateOrderId();
        event.userId = request.getUserId();
        event.amount = request.getAmount();
        event.begin();
        
        try {
            // 业务逻辑
            return orderRepository.save(order);
        } finally {
            event.duration = System.currentTimeMillis() - event.getStartTime();
            event.commit();
        }
    }
}

八、实战:CPU 飙高问题排查流程

8.1 完整排查流程

CPU 飙高排查流程:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  1. 定位问题进程                                              │
│     jps / top                                                │
│                                                             │
│  2. 初步分析(Arthas)                                         │
│     arthas-boot -> thread -n 3                               │
│     快速定位热点线程                                           │
│                                                             │
│  3. 深度分析(async-profiler)                                │
│     ./profiler.sh -d 30 -f cpu-flame.html <pid>              │
│     生成火焰图,精准定位热点函数                                │
│                                                             │
│  4. 全链路分析(JFR)                                          │
│     jcmd <pid> JFR.start duration=60s filename=profile.jfr   │
│     分析 GC、锁、IO 等综合因素                                 │
│                                                             │
│  5. 动态追踪(BTrace)                                         │
│     监控热点方法的参数、返回值、耗时                            │
│                                                             │
│  6. 验证修复                                                  │
│     重新运行 async-profiler 确认优化效果                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8.2 实战案例

场景:线上订单服务 CPU 突然飙到 100%

# 步骤 1:定位进程
$ jps
12345 OrderService

# 步骤 2:Arthas 初步分析
$ ./as.sh 12345
thread -n 3
# 发现大量线程卡在 OrderService.calculate()

# 步骤 3:async-profiler 深度分析
$ ./profiler.sh -d 30 -f cpu-flame.html 12345
# 火焰图显示 calculate() 中 BigDecimal 运算占用 80% CPU

# 步骤 4:JFR 全链路分析
$ jcmd 12345 JFR.start duration=60s filename=profile.jfr
# JMC 分析发现频繁的 BigDecimal 运算导致大量对象分配

# 步骤 5:修复
# 将 BigDecimal 运算改为使用 primitive 类型
# 添加缓存避免重复计算

# 步骤 6:验证
$ ./profiler.sh -d 30 -f cpu-flame-after.html 12345
# CPU 使用率从 100% 降至 20%

九、工具选型指南

9.1 按场景选择

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  场景                推荐工具               理由              │
│  ─────────────────   ──────────────────    ──────────────    │
│  日常快速排查         Arthas                 全能、易用        │
│  远程监控             jconsole/jvisualvm     无需登录          │
│  CPU/内存性能分析     async-profiler         高精度、火焰图    │
│  动态追踪             BTrace                 安全、只读        │
│  全链路分析           JFR                    零损耗、生产级    │
│  复杂问题定位         JFR + async-profiler   组合拳            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

9.2 学习建议

学习路径:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  入门阶段(1-2 周)                                           │
│  ├── 掌握 Arthas 常用命令                                      │
│  └── 熟悉 jconsole/jvisualvm 基础功能                          │
│                                                             │
│  进阶阶段(2-4 周)                                           │
│  ├── 掌握 async-profiler 四种采样模式                           │
│  ├── 学会解读火焰图                                            │
│  └── 掌握 JFR 基本使用                                         │
│                                                             │
│  高级阶段(持续学习)                                          │
│  ├── 掌握 BTrace 脚本编写                                      │
│  ├── 学会自定义 JFR 事件                                        │
│  └── 建立系统化的问题排查方法论                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

十、完整示例工程

我为你准备了一个专门用于练习这些工具的示例工程:

10.1 项目结构

java-diagnostic-tools-demo/
├── src/main/java/com/example/demo/
│   ├── DemoApplication.java
│   └── controller/
│       └── DiagnosticController.java
├── src/main/resources/
│   └── application.yml
├── scripts/
│   ├── arthas-commands.txt
│   ├── async-profiler-cpu.sh
│   ├── async-profiler-alloc.sh
│   ├── async-profiler-lock.sh
│   ├── jfr-record.sh
│   └── btrace-script.java
└── pom.xml

10.2 模拟问题端点

@RestController
@RequestMapping("/api")
public class DiagnosticController {
    
    // CPU 热点:大量计算
    @GetMapping("/cpu-hotspot")
    public String cpuHotspot(@RequestParam int iterations) {
        double result = 0;
        for (int i = 0; i < iterations; i++) {
            result += Math.sin(i) * Math.cos(i) * Math.tan(i);
        }
        return "Result: " + result;
    }
    
    // 内存分配:创建大量对象
    @GetMapping("/memory-burst")
    public String memoryBurst(@RequestParam int count) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            list.add(UUID.randomUUID().toString());
        }
        return "Created " + count + " objects";
    }
    
    // 锁竞争:模拟并发锁等待
    @GetMapping("/lock-contention")
    public String lockContention() throws InterruptedException {
        synchronized (this) {
            Thread.sleep(100);
        }
        return "Done";
    }
}

10.3 使用脚本

# 启动应用
mvn spring-boot:run

# 模拟 CPU 热点
curl "http://localhost:8080/api/cpu-hotspot?iterations=10000000"

# 模拟内存分配
curl "http://localhost:8080/api/memory-burst?count=100000"

# 模拟锁竞争(并发请求)
for i in {1..100}; do
    curl "http://localhost:8080/api/lock-contention" &
done

# async-profiler CPU 分析
./scripts/async-profiler-cpu.sh <pid>

# JFR 记录
./scripts/jfr-record.sh <pid>

十一、总结

11.1 工具对比总结

┌──────────────┬─────────────────────────────────────────────┐
│ 工具         │ 核心优势                                      │
├──────────────┼─────────────────────────────────────────────┤
│ Arthas       │ 全能、易用、快速排查                          │
├──────────────┼─────────────────────────────────────────────┤
│ jconsole     │ 零依赖、远程监控、基础功能                    │
├──────────────┼─────────────────────────────────────────────┤
│ jvisualvm    │ 可视化、插件丰富、进阶监控                    │
├──────────────┼─────────────────────────────────────────────┤
│ async-profiler│ 高精度、火焰图、四种采样模式(核弹级推荐)   │
├──────────────┼─────────────────────────────────────────────┤
│ BTrace       │ 安全、只读、动态追踪                          │
├──────────────┼─────────────────────────────────────────────┤
│ JFR          │ 零损耗、生产级、全链路事件记录                │
└──────────────┴─────────────────────────────────────────────┘

11.2 核心要点

  1. Arthas 是基础:日常排查的首选工具
  2. async-profiler 是进阶:深度性能分析的必备工具
  3. JFR 是生产级:零损耗的全链路监控
  4. 工具组合使用:没有最好的工具,只有最适合的组合

💡 互动话题:你在生产环境中最常用的诊断工具是什么?遇到过哪些棘手的问题?欢迎在评论区分享你的经验!


标题:Arthas 之外还有这些 Java 诊断利器,第 3 个绝了
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/07/26/1785051978531.html
公众号:服务端技术精选

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程

取消