JProfiler
JProfiler 是 ej-technologies 出品的 Java 性能分析工具,提供 CPU 分析、内存分析、线程分析、数据库分析四大维度的可视化剖析。适合本地或 CI 环境的深度性能调优,与 Arthas(线上动态诊断)互补。
工具对比
| JProfiler | Arthas | jstack / jcmd | |
|---|---|---|---|
| 分析深度 | 深度(可视化调用树、堆快照) | 中等(命令行动态观察) | 基础(快照/统计) |
| 使用场景 | 本地/预发性能调优 | 生产环境无侵入诊断 | 生产快速采集数据 |
| 是否需重启 | 否(attach 模式) | 否 | 否 |
| 界面 | 图形化 | 命令行 | 命令行 |
| 费用 | 商业(有试用) | 免费开源 | JDK 内置免费 |
安装与启动模式
本地模式(直接启动 Java 应用)
在 JProfiler 界面:Session → New Session → Profile a local Java application,选择主类或 JAR,JProfiler 以 Agent 方式注入后启动。
Attach 模式(挂载到运行中进程)
Session → Attach to running JVM,选择目标 PID,无需重启应用。适合已运行的 Spring Boot 实例。
远程模式(服务器 / Docker / K8s)
在目标 JVM 启动参数中加入 JProfiler Agent:
# 下载对应平台的 Agent(与本机 JProfiler 版本一致)
# 启动时注入
java -agentpath:/opt/jprofiler/bin/linux-x64/libjprofilerti.so=port=8849 \
-jar app.jarJProfiler 客户端:Session → New Session → Profile a remote JVM,填写服务器地址和端口 8849。
Docker 远程
# Dockerfile 中预置 Agent
COPY jprofiler/linux-x64 /opt/jprofiler/
ENTRYPOINT ["java", \
"-agentpath:/opt/jprofiler/libjprofilerti.so=port=8849,nowait", \
"-jar", "app.jar"]# 映射端口
docker run -p 8849:8849 my-appIDEA 集成
安装 JProfiler IDEA 插件后,Run/Debug 工具栏出现 JProfiler 按钮,一键在 IDEA 中对当前运行配置启动 JProfiler 分析。
CPU 分析
CPU 分析用于定位热点方法(最耗 CPU 的代码路径)。
采样(Sampling)vs 探针(Instrumentation)
| 方式 | 原理 | 开销 | 精度 |
|---|---|---|---|
| Sampling | 定期抓取线程栈快照 | 低(推荐首选) | 统计精度(高频方法可见) |
| Instrumentation | 字节码注入,记录每次方法进出 | 高(性能影响显著) | 精确到每次调用 |
推荐流程:先 Sampling 定位热点区域 → 再对热点包用 Instrumentation 精确分析。
调用树(Call Tree)
▼ main thread 100%
▼ OrderService.createOrder() 98%
▼ InventoryService.checkStock() 85% ← 热点
▼ JdbcTemplate.query() 80%
▼ HikariCP.getConnection() 40% ← 连接池瓶颈
▼ PaymentService.charge() 12%
- 从根节点向下展开,找占比最高的叶子方法
- 勾选
Filter → Exclude JDK methods聚焦业务代码 - 右键方法 →
Find in Source:直接在 IDEA 中定位源码
热点视图(Hot Spots)
按自身耗时(不含子方法)排序,直接暴露最慢的具体方法:
Method Self Time Invocations
InventoryDao.countBySkuId() 38% 1,024
StringUtils.formatPrice() 12% 50,320
JsonMapper.serialize() 8% 8,192
内存分析
内存分析用于定位内存泄漏和对象分配热点。
堆浏览器(Heap Walker)
手动触发堆快照:Memory → Heap Walker → Take Heap Snapshot
对象类 实例数 大小 占比
byte[] 128,340 520 MB 62%
String 98,230 120 MB 14%
HashMap$Entry 42,100 67 MB 8%
通过 References 视图追踪对象的引用链,找到持有大对象的 GC Root:
GC Root → ThreadLocalMap → Entry → UserSession → List<LogRecord> → 泄漏点
分配记录(Allocation Recording)
开启后记录每个对象的分配调用栈,找到在哪段代码分配了大量对象:
Session → Start Recording Allocations
... 复现问题场景 ...
Session → Stop Recording Allocations
分配调用树示例:
▼ http-nio-exec-1
▼ LogService.buildContext() 42,000 alloc/s
▼ String.format() 38,000 alloc/s ← 频繁创建 String
内存泄漏检测
- 记录初始堆快照
- 重复执行可疑操作 N 次
- 触发 Full GC(
Memory → Run GC) - 记录第二个堆快照
- Compare Snapshots:只有持续增长且 GC 后不释放的对象才是泄漏候选
常见泄漏来源:
ThreadLocal未remove()- 静态集合(
static List/Map)无限追加 - 事件监听器注册后未注销
- 内部类持有外部类引用
与 垃圾回收 中的 GC 日志结合分析效果更好。
线程分析
线程历史(Thread History)
时间轴展示每个线程的状态变化:
| 颜色 | 状态 |
|---|---|
| 绿色 | RUNNABLE(运行中) |
| 红色 | BLOCKED(等待锁) |
| 橙色 | WAITING / TIMED_WAITING |
| 蓝色 | NET(网络 I/O) |
| 灰色 | SLEEP |
红色区域过长 → 锁竞争,点击查看等待哪把锁,持锁线程是谁。
Monitor 使用情况
Threads → Monitors & Locks 查看各 Monitor 的争用热度,定位锁竞争瓶颈:
Lock: com.example.service.OrderService@1a2b3c
Waiting threads: 12
Average wait time: 230ms
Held by: http-nio-exec-5 (currently executing OrderService.updateStatus())
与 jstack 中死锁检测配合,JProfiler 可可视化查看历史死锁。
线程 Dump
Threads → Thread Dump:等价于 jstack,但可直接在 JProfiler 中高亮标注阻塞线程。
数据库 / JDBC 分析
JProfiler 可拦截 JDBC 调用,统计 SQL 执行情况:
Session → Start Recording JDBC
... 触发业务操作 ...
Session → Stop Recording JDBC
视图:
SQL Statement Count Total Time Avg Time
SELECT * FROM orders WHERE user_id=? 1,024 8.2s 8ms
UPDATE inventory SET stock=? WHERE id=? 1,024 5.1s 5ms ← N+1 问题
N+1 SQL 直接暴露(相同语句执行上千次),定位后结合 JPA与Hibernate 的批量查询或 JOIN 优化。
典型排查场景
场景 1:接口 P99 延迟高
- Attach 到目标进程,开启 CPU Sampling
- 用压测工具(JMeter)模拟负载
- 在 Call Tree 中找调用链最慢的分支
- 切换 Instrumentation 对热点包精确计时
- 定位到
InventoryDao.checkStock()无索引全表扫描 → 加索引
场景 2:内存持续增长,Full GC 后不回落
- Attach,间隔 10 分钟抓两次 Heap Snapshot
- Compare:找增量最大的对象类型
- Heap Walker → References:追溯 GC Root 引用链
- 发现
static ConcurrentHashMap<String, Session>未清理过期 Session - 添加定时清理逻辑
场景 3:请求偶发卡顿(无 CPU 耗用)
- Thread History 查看卡顿时间窗口
- 所有请求线程均为橙色(WAITING)
- Monitor 视图:等待
HikariPool的getConnection()锁 - 连接池满,扩大
maximumPoolSize或排查慢 SQL 导致连接不归还
JVM 参数与 GC 分析
JVM → GC Activity 面板展示:
- GC 事件时间轴(Minor GC / Full GC)
- 各代内存变化曲线
- GC 停顿时间
配合 性能调优 中的 JVM 参数调整,验证调整效果。
常用配置技巧
过滤器(Filter)
只记录业务包,排除 JDK / 框架噪音:
Settings → Filter Settings
Include: com.example.*
Exclude: org.springframework.*, com.sun.*, java.*
快照保存与对比
File → Save Snapshot (.jps)
File → Open Snapshot
Compare Snapshots(内存视图支持差量对比)
Trigger(自动触发快照)
在特定条件下自动保存快照(如 GC 后内存超阈值、线程 Deadlock 检测到时):
Session → Triggers → Add Trigger
Condition: Memory > 80%
Action: Save Heap Snapshot