IWA
2025-11-28
点 赞
0
热 度
10
评 论
0

Java 性能优化实战 第 2 篇:如何定位性能瓶颈


——从 Linux → JVM → 业务代码的完整排查流程(强收藏)

性能优化的第一步不是“优化”,而是 定位瓶颈
很多程序员一遇到问题就想调 GC、加线程池、加 Redis、加机器——这其实是方向错误。

真正的性能高手,首先会做的是:

找到最关键的那个瓶颈点,而不是盲目调整。

本篇给你一套 “线上必用、面试必问、运维认可” 的性能排查流程。


🧭 一、性能瓶颈定位的总思路

性能问题一般分 5 大类:

CPU 问题
内存 / GC 问题
线程问题
IO / 网络问题
DB(MySQL / Redis)瓶颈

为了定位这些问题,我们可以用 三层排查法

第 1 层:Linux 层排查 → 系统资源是否异常?
第 2 层:JVM 层排查 → GC、线程、内存是否异常?
第 3 层:业务层排查 → SQL / Redis / 代码逻辑 / 架构瓶颈?

掌握这三层,你能解决 90% 的线上问题。


🔥 二、第一层:Linux 层排查(快速判断方向)

当系统出现:

  • CPU 100%

  • RT 延迟升高

  • QPS 急剧下降

  • 线程阻塞

  • 服务卡顿

先不要看 JVM,先看系统层数据。

✔ 1. top —— CPU / Load 大盘情况

top -Hp <pid>

重点关注:

(1) load average 是否异常

  • load > CPU 核数 × 1.5 → 暂时过载

  • load > CPU 核数 × 2 → 严重过载

(2) CPU 占比

  • us(用户态)太高 → Java 计算/循环

  • sy(内核态)太高 → IO 问题

  • si(软中断)太高 → 网络瓶颈

  • wa(IO wait)太高 → IO/磁盘阻塞

(3) 哪个线程 CPU 最高?

top -Hp <pid>

找到线程号(TID)后,再转换成 16 进制:

printf "%x\n" <tid>

之后我们可以用 jstack 找到具体 Java 线程(后面讲)。


✔ 2. vmstat —— 内存、上下文切换

vmstat 1 5

重点看:

  • cs(context switch)上下文切换是否过高

    超过 4000/s 基本不正常

  • si / so(swap in/out)是否发生 swap

  • r (运行队列)是否过长

CPU 上下文切换太多,往往是:

  • 线程太多

  • 锁竞争

  • 频繁 GC


✔ 3. iostat —— IO 是否瓶颈

iostat -x 1

重点看:

  • %util → IO 设备利用率

    长期 90%以上 = IO 满了

  • await → IO 延迟

IO 问题往往是:

  • 大量日志写入

  • MySQL 写压力

  • Redis AOF 写压力

  • 大文件读写


✔ 4. netstat —— 网络层是否阻塞

netstat -natp | grep ESTABLISHED | wc -l

如果连接数爆炸 = 连接池问题、网关问题、雪崩效应。

netstat -s

查看网络丢包、重传等信息。


🔥 三、第二层:JVM 层排查(GC、内存、线程)

Linux 层确认方向后,就进入 JVM 层排查。

核心工具:

jstat
jmap
jstack
jcmd
Arthas

✔ 1. jstat —— 查看 GC 情况(必用)

jstat -gc <pid> 1000

重点看:

字段

意义

YGC

Minor GC 次数

YGCT

Minor GC 耗时

FGC

Full GC 次数

FGCT

Full GC 耗时

常见问题:

  • Full GC 频繁 → 内存不足 / 大对象 / 元空间不足

  • Minor GC 太频繁 → 新生代太小 / 对象创建太多


✔ 2. jmap —— Dump 内存分析

jmap -dump:live,format=b,file=heap.hprof <pid>

用 MAT 分析:

  • 哪些对象占用内存最多

  • 是否发生内存泄漏

  • ThreadLocal 泄漏

  • 缓存未清理

  • 大集合无边界增长


✔ 3. jstack —— 线程分析

jstack <pid> > stack.log

结合 top 找出的 TID:

能看到哪些线程问题?

  • 死锁(Java 自带检测)

  • 等待锁(BLOCKED)

  • GC 线程异常

  • 任务堆积

  • 线程池任务执行缓慢(大量 RUNNABLE)

  • 频繁上下文切换


✔ 4. Arthas —— 强烈推荐的排查神器

arthas-boot

核心命令:

命令

用途

thread

查看最耗 CPU 线程

dashboard

GC/线程堆栈/LT 总览

trace

代码级耗时分析(神器)

jad

反编译类

watch

实时监控方法变量

heapdump

dump 内存

Arthas 的 trace 能精确查出哪个方法最浪费时间,堪称神器。


🔥 四、第三层:业务瓶颈定位(SQL/Redis/代码/架构)

当系统资源和 JVM 方向明确后,就要深入业务层。


✔ 1. 数据库瓶颈排查

慢查询日志(重点)

开启 slow log,捕获 >200ms 的 SQL。

常见问题:

  • 没有索引

  • 全表扫描

  • 联表太多

  • 函数操作字段

  • limit 大分页

  • 高并发写入

80% 的性能问题都出在 SQL。


✔ 2. Redis 瓶颈排查

重点查:

  • 热 Key

  • 大 Key(hash/set > 5W item)

  • pipeline 未使用

  • LUA 脚本未优化

  • 锁(SETNX)导致阻塞

  • Redis 连接池耗尽

可以用:

redis-cli --latency
monitor
slowlog get 10

✔ 3. 线程池排查

常见错误:

  • 核心线程数太少(吞吐低)

  • 最大线程太大(CPU 上下文切换)

  • 队列太大(堆内存撑爆)

建议监控线程池:

  • ActiveCount

  • QueueSize

  • CompletedTaskCount


✔ 4. 代码逻辑瓶颈

常见问题:

  • 使用 synchronized 锁大范围代码

  • 复杂循环

  • N+1 查询

  • 无限重试

  • 异常频繁抛出(非常耗性能)


🌟 五、一套标准的瓶颈定位流程(实战可用)

下面这套流程足够你解决大多数线上问题:


📌 Step 1:top 判断资源方向

CPU 是否异常?
IO/Load 是否高?
哪个线程最耗 CPU?


📌 Step 2:jstack 定位最耗 CPU 的 Java 线程

死循环?锁等待?线程池阻塞?


📌 Step 3:jstat 看 GC

FullGC 是否频繁?
GC 耗时是否过高?


📌 Step 4:jmap dump 内存

找到大对象泄漏。


📌 Step 5:Arthas trace 查代码耗时

trace 某个 Controller 或 Service:

trace com.xxx.OrderService createOrder

直接看到哪段代码最慢!


📌 Step 6:数据库 / Redis 排查

慢 SQL、慢日志、大 Key、热 Key。


📌 Step 7:线程池 / 调用链监控

是否有任务堆积?
是否存在雪崩?
是否出现服务链路阻塞?


🎯 六、结语:定位是优化的前置条件

没有定位,所有优化都是凭感觉。
有了这篇文章的流程,你的排查能力会立刻提升一个等级。

下一章会正式进入 JVM 深水区:



用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

IWA

infp 调停者

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

文章目录

IWA的艺术编程,为您导航全站动态

40 文章数
9 分类数
10 评论数
34标签数
最近评论
IWA

IWA


👍

M丶Rock

M丶Rock


😂

M丶Rock

M丶Rock


感慨了

M丶Rock

M丶Rock


厉害了

M丶Rock

M丶Rock


6666666666666666666