高阶题 · 性能优化
1 题 · 平均 2.5/5 · 高阶题
先建立因果链,再谈工具:
```
高并发争抢同一个 monitor
│
▼
synchronized 膨胀为重量级锁 → 未获锁线程 ObjectMonitor::enter
│
▼
线程 park/unpark → JVM 调用 futex 系统调用(内核态)
│
▼
大量上下文切换(cs 暴增)+ 可运行队列变长
│
▼
CPU sys 占比飙升 + load average 飙升
```
**关键认知**:
- **load 高 ≠ CPU 用满**:load 统计 **R(可运行)+ D(不可中断)** 状态线程。大量线程在 `BLOCKED`(等待锁)其实是 **S 状态**,不直接计入 load;但锁释放后线程被唤醒争抢,出现"唤醒风暴 + 自旋",R 队列被挤爆,load 随之飙升。
- **sys 高是内核态开销**:这里的系统调用就是 `futex(FUTEX_WAIT/FUTEX_WAKE)`。锁竞争越激烈,park/unpark 越频繁,`futex` 调用越多。
- **`AtomicLong` 的额外贡献**:8 核上所有线程 CAS 同一个 `value`,缓存行在核间来回失效(**false sharing / cache line bouncing**),失败线程自旋重试 → user 高 + 无谓消耗;若该字段与相邻热点字段共享缓存行,伪共享进一步放大。
**定位路径(落到命令与指标)**:
| 步骤 | 命令 | 看什么 |
|---|---|---|
| 1. 确认 CPU/负载构成 | `top` → `1`(分核)、`uptime` | `%sy` 高、load 与核数比 |
| 2. 上下文切换速率 | `vmstat 1` / `pidstat -w 1 -p <pid>` | `cs`(context switch/s)、`cswch/s`(自愿切换)暴增 |
| 3. 定位热点线程 | `top -Hp <pid>` → 转十六进制 `printf '%x' <tid>` | 找到吃 CPU 的线程 |
| 4. 线程栈深挖 | `jstack <pid> > stack.txt`、`arthas thread -b` | 大量 `BLOCKED (on object monitor)` 指向同一地址;`thread -b` 直接给"阻塞其他线程的元凶" |
| 5. 锁竞争量化 | `arthas monitor -c 5 com.xxx.Class method`、JFR(`-XX:StartFlightRecording`)锁事件 | 阻塞次数/平均阻塞耗时 |
| 6. 内核态采样 | `perf top -p <pid>`、`perf record -g` | `futex`、`__lll_lock_wait`、`sys_futex` 占比 |
| 7. 应用层链路 | SkyWalking / 接口 RT 监控 | 确认慢在哪个接口、哪个节点(题中回答已覆盖) |
> 题中回答的 skywalking + jstack 只是第 4、7 步;**缺了第 2、3、5、6 步**,所以无法量化"锁竞争 → 系统调用"的因果。
| 层 | 手段 | 预期效果 |
|---|---|---|
| **锁粒度** | ① 缩小临界区,把非共享操作移出 `synchronized`;② 读写分离:缓存刷新用 `volatile` + **不可变快照**替换(读无锁,写整体替换引用) | 临界区变短 → 锁持有时间下降 → 阻塞线程数骤减 → cs 下降 |
| **无锁化** | ① `AtomicLong` → **`LongAdder`**(Striped64 分段 CAS,把竞争分散到多个 cell);② 缓存行填充 `@Contended`(需 `-XX:-RestrictContended`)解决伪共享;③ 单写者模式 | CAS 失败自旋大幅减少;消除 cache line bouncing → user/sys 双降,吞吐提升 |
| **线程模型** | ① 同步刷新 → **异步批量刷新**(定时任务 / single-flight 合并重复刷新请求);② 缓存刷新交给独立单线程(single-writer),业务线程只读;③ 若计数无需强一致,改为本地聚合 + 定时刷库 | 从"每请求争锁"变"批量/单写者",争抢源消失 |
**`LongAdder` 原理**:`AtomicLong` 的 `value` 是单点 `volatile`,所有线程 CAS 同一地址;`LongAdder` 维护 `base + Cell[]`,线程按 `threadLocalRandomProbe` 散列到不同 cell,冲突时扩容,求和时才汇总。8 核下写吞吐可提升数倍。
1. **常驻指标**:`sys%`、`cs/s`、`cswch/s`、load1/5/15、锁阻塞时间(`ThreadMXBean.getThreadInfo` 的 `blockedCount/blockedTime`、JFR 锁事件)、`LongAdder` 所在监控项
2. **告警阈值**:`cs/s` 突增、`sys% > 40%`、`jstack BLOCKED` 线程数 > X 时触发
3. **压测回归**:上线前用压测(JMeter/Gatling)复现 6000 QPS,观测 cs/sys 曲线与 P99;设性能基线卡点
4. **可观测**:arthas `monitor`/`watch` 常驻排查;JFR 采样回放;SkyWalking 慢接口拓扑
5. **架构兜底**:无状态化 + 横向扩容降低单机 QPS;热点计数下沉到 Redis/本地分片;必要时限流保护
---