Java 性能问题定位:从系统指标到应用排查

Java 性能问题定位:从系统指标到应用排查 这篇笔记参考了阿里云相关博客中的排查思路,原文写得很细,我在此基础上按自己的使用习惯重新做了整理。 线上 Java 应用一旦出现响应变慢、资源飙升或进程异常,排查范围很容易越铺越大,下面会从业务代码、CPU、线程、内存、GC、磁盘 I/O、网络 I/O 和常用命令几个角度,把常见的定位方法串起来。 实际的性能问题很少只对应一个异常指标,更多时候是系统层、组件层与应用层互相影响后的结果;排查时可以从最贴近业务的应用层入手,拿到初步方向后,再回到系统指标中交叉验证。 性能优化工具图谱 ├── 系统层 │ ├── CPU │ │ ├── 🚩 CPU利用率(top/vmstat/sar/dstat) │ │ ├── 🚩 CPU平均负载(top/uptime) │ │ └── 上下文切换次数(pidstat/vmstat/dstat) │ ├── 内存 │ │ ├── 全局内存使用 │ │ │ ├── 🚩 已用/剩余/可用内存(free/vmstat/sar) │ │ │ └── 缓冲区/缓存(pcstat/cachestat/cachetop) │ │ └── 进程内存使用 │ │ ├── 🚩 虚拟内存/常驻内存/共享内存(top/ps/pidstat) │ │ ├── 🚩 SWAP 内存使用/换入换出速度(top/free/vmstat/sar) │ │ ├── 缺页异常(ps/pidstat) │ │ └── 内存分布(pmap/jmap) │ ├── 磁盘 │ │ ├── 空间容量(df/du) │ │ ├── 🚩 吞吐量/磁盘 I/O 使用率(iostat/dstat/sar) │ │ └── 缓冲区/缓存(pcstat/cachestat/cachetop) │ └── 网络 │ ├── 🚩 吞吐量(sar) │ ├── 网络延迟(ping) │ ├── 🚩 网络连接数/错误数(netstat/ss/sar) │ └── 网络抓包(tcpdump/wireshark) ├── 组件层 │ ├── 数据库 │ │ ├── SQL 调优 │ │ ├── 🚩 索引调优 │ │ └── 连接池配置 │ ├── 网络 IO │ │ ├── I/O 调度模型 │ │ ├── 序列化框架 │ │ └── 线程调度模型 │ ├── Web 容器 │ │ └── 线程池配置 │ └── 缓存/MQ…… └── 应用层 ├── 线程 │ ├── 死锁检查(jstack/arthas) │ ├── 🚩 线程状态分布(jstack/arthas) │ ├── 锁竞争分布(jstack/arthas) │ ├── 🚩 代码执行热点(jprofiler/zprofiler) │ ├── 🚩 占用 CPU 较重的线程(top + pidstat + jstack) │ └── 代码追踪(btrace/housemd/greys/arthas) ├── 内存 │ ├── 内存分配 │ │ ├── 常驻内存/虚拟内存(top) │ │ ├── 对象分配热点(jprofiler/zprofiler) │ │ ├── 🚩 堆内对象分布(jmap/zprofiler/MAT) │ │ ├── 类加载相关(jstat/greys/arthas) │ │ ├── 🚩 内存泄漏(gperf/MAT/zprofiler) │ │ └── 堆外内存(jmap + MAT + NMT + gdb + perf) │ └── 垃圾回收 │ ├── GC 线程使用(jinfo) │ ├── 对象晋升年龄(gclog) │ ├── 🚩 GC 的频率和时间(jstat/gclog) │ ├── 垃圾回收器类型/JVM 参数(jinfo/jcmd) │ └── 🚩 堆大小设置及分区大小(jinfo/jstat) ├── 网络 │ ├── 带宽使用 │ ├── 流量异动 │ └── 网络分区 └── ★ 业务(日志、监控…) ├── 🚩 代码逻辑 ├── 远程调用 └── 架构设计 CPU 与线程 观察 CPU 时,最常看的指标是下面三项,常用工具包括 top、ps、uptime、vmstat 和 pidstat。 ...

2026年5月3日 · 8 分钟

ThreadLocal 原理与线程上下文传递

ThreadLocal 是什么 ThreadLocal 用于保存线程私有变量。 同一个 ThreadLocal 可以被多个线程访问,但每个线程读取和修改的都是自己保存的值,线程之间默认互不影响。 常见用途包括: 保存当前用户信息; 保存 TraceId; 保存租户或请求上下文; 在线程内部隐式传递参数。 private static final ThreadLocal<String> USER_CONTEXT = new ThreadLocal<>(); 调用 set() 时,数据并不是直接存入 ThreadLocal 对象,而是存入当前线程内部的 ThreadLocalMap: Thread └── ThreadLocalMap ├── ThreadLocal A → Value A └── ThreadLocal B → Value B 一个线程可以使用多个 ThreadLocal,但这些数据都保存在当前线程自己的 ThreadLocalMap 中。 ThreadLocal 为什么会内存泄漏 ThreadLocalMap 中的 Entry 可以简化为: static class Entry extends WeakReference<ThreadLocal<?>> { Object value; } 其中: Key,也就是 ThreadLocal,是弱引用; Value 是强引用。 如果外部不再引用某个 ThreadLocal,GC 可以回收 Key,此时 Map 中可能留下: ...

2025年4月2日 · 3 分钟

Java 设计模式笔记

设计模式 举个例子,比如我们有个导出功能,按照不同的格式导出数据,比如 CSV、Excel、JSON 等;比如我们需要导入 execl 文件,需要解析文件内容,针对不同的格式,需要不同的解析方式;再比如我们有一个给用户发送邮件功能,有可能需要用 gmail, qq, 163 等邮箱服务商,手机验证码服务也是如此。如果我们使用 if-else 或者 switch-case 来实现,代码会变得很臃肿,而且扩展性很差,多一个导出为其他格式的需求,就需要大量修改代码。 处理之前的代码可能长这样: /** * @param filePath 文件路径(含文件名) * * 导出 CSV 和 Excel 的需求 */ public void export(String filePath) { String fileType = filePath.substring(filePath.lastIndexOf(".") + 1); if ("csv".equals(fileType)) { // 导出 CSV 的具体代码,以下省略100行 } else if ("excel".equals(fileType)) { // 导出 Excel 的具体代码,以下省略100行 } else { throw new IllegalArgumentException("不支持的文件类型:" + fileType); } } 策略模式 策略模式的主要作用是用来提升一些代码的复用性的,或者解决代码中出现很多 if-else 语句的问题。 ...

2024年1月21日 · 4 分钟