性能优化案例:CPU 飙升排查
监控系统告警:服务器 CPU 使用率突然飙升至 100%,持续 5 分钟。应用响应变慢,用户开始投诉。
问题背景
服务器状态:
- CPU:100%(正常 30-40%)
- 内存:正常
- GC:正常
- QPS:正常(略低)
排查步骤
第一步:确认 CPU 热点
第二步:生成火焰图
第三步:分析火焰图
打开 cpu.html,火焰图显示:
问题定位到 TextProcessor.process() 方法。
第四步:查看代码
TextProcessor.java
根因分析
- 循环中调用
charAt:每次调用可能创建 Character 对象 - 正则每次编译:
matches()每次都编译正则
修复方案
TextProcessor.java
修复效果
问题总结
问题根因
charAt()在循环中调用:虽然 charAt 本身很快,但结合业务逻辑可能成为瓶颈Pattern.matches()每次编译:正则编译开销很大
优化策略
排查流程总结
经验总结
教训一:正则要预编译
教训二:字符串操作要小心
字符串操作是 CPU 热点的高发区:
+拼接 vs StringBuildersplit()vs StringTokenizerreplace()vs replaceAll
教训三:火焰图是 CPU 优化的利器
火焰图可以直观展示 CPU 时间的分布:
- 宽度 = CPU 时间占比
- 尖顶 = 真正的热点
本章小结
CPU 飙升排查的标准流程:
- top 查看进程:确认是 Java 进程
- 火焰图分析:定位热点方法
- 代码分析:找到性能问题
- 优化代码:针对性优化
- 验证效果:确认优化有效
延伸思考
为什么 charAt 会成为热点?
charAt() 本身很快,但结合以下情况会成为热点:
- 循环中调用:高频调用放大影响
- 与其他慢操作混合:木桶效应
- 热点数据特性:长字符串影响更大
优化时应该用 profiling 确认瓶颈,而不是凭感觉优化。