本文是线上问题实战录系列的第 3 篇
叙事框架:现象 → 排查过程 → 根因 → 修复 → 预防
2026 年 6 月 18 日下午 3 点 15 分,生产监控告警群突然炸了:
auth-service CPU 使用率持续 100%,已持续 12 分钟/api/v1/auth/validate p99 响应时间从 50ms 飙升到 2.3 秒
登录生产服务器,执行 top,发现 PID=24589 的 Java 进程占满了 1 个核,RSS 4.5GB:

top -Hp 24589
TID=24650 占 99% CPU,其他 186 个线程几乎空闲。转十六进制:printf '%x\n' 24650 → 0x605a。

jstack 24589 > /tmp/jstack.txt
grep -A 40 'nid=0x605a' /tmp/jstack.txt
输出显示线程栈卡死在正则表达式匹配中,89 帧都在 Pattern$BmpCharProperty.match 之间来回跳——典型的正则回溯:

一秒就查出来了,和 jstack 结果一致,但快得多:

String.matches(pattern) 等价于 Pattern.compile(pattern).matcher(input).matches()。每次调用都重新编译同一个正则表达式,在高并发下 CPU 直接打满:

今天上午 10 点上线的代码变更引入了 isValidPhoneV1。该方法在每次收到验证请求时都通过 String.matches() 编译正则表达式:
String.matches() → Pattern.compile() → 编译正则 → 创建 Pattern 对象 → 创建 Matcher 对象 → 执行匹配
↑ 每次请求重复执行整个过程
在高并发下(100 TPS),不断重复的 Pattern.compile() 导致 CPU 完全被正则编译和匹配占据。
将正则表达式预编译为 static final 常量:




| 指标 | V1(修复前) | V2(修复后) | 提升倍数 |
|---|---|---|---|
| TPS | 6,067 | 28,340 | 4.67x |
| 平均响应时间 | 16.48ms | 3.53ms | ↓78% |
| P99 响应时间 | 196ms | 28ms | ↓86% |
| CPU 使用率 | 99.2% | 12.3% | ↓86% |
String.matches()、Pattern.matches()、String.split() 等会触发隐式编译的方法static final Pattern 常量top -Hp → printf '%x\n' → jstack → grep nidarthas thread -n 3top -b -n 1 | head -20 # 查看进程 CPU 排行
top -b -H -p <pid> -n 1 | head -25 # 查看线程 CPU 排行
printf '%x\n' <tid> # TID 转十六进制
jstack <pid> > /tmp/jstack.txt # 导出线程栈
grep -A 40 'nid=0x<nid>' /tmp/jstack.txt # 查看指定线程栈帧
thread -n 3 # Arthas 查看最忙线程
stack <class> <method> # Arthas 查看方法调用链
grep -n 'isValidPhone\\|String.matches\\|Pattern' src/main/java/com/opencao/demo/UserValidator.java # 定位问题代码
ab -n 10000 -c 100 -T 'application/json' -p request.json http://auth-service:8080/api/v1/auth/validate/v1 # 修复前
ab -n 10000 -c 100 -T 'application/json' -p request.json http://auth-service:8080/api/v1/auth/validate/v2 # 修复后
mvn test -Dtest=RegexBenchmark -pl . # JMH 基准测试
📖 全文带可复现 Demo 和排查截图 🔗 个人博客:https://opencao.cn 📺 公众号:Ai拆代码的曹操 🌟 知识星球:源阅会 (82877104)