高并发下异常处理性能损耗主因是栈追踪生成、对象分配和锁竞争,需在秒杀等高频路径用result替代异常抛出,禁用fillinstacktrace或复用静态异常实例,并通过p95和火焰图精准测量优化效果。

高并发下异常处理的性能损耗主要来自栈追踪生成、对象分配和锁竞争,不是“少用异常”就够,关键是在秒杀、下单等高频路径上做精准控制和替代。
聚焦高频业务异常,优先用结果对象代替抛出
库存不足、重复下单、资格失效这类异常每秒可能触发数万次,每次抛出都执行 fillInStackTrace(),20层调用栈耗时可达 30–50μs。真实压测中,P95 耗时差值常超 40μs/次——百万请求就多耗 40 秒 CPU。
- 把
throw new StockNotEnoughException()改为return Result.fail("stock_not_enough") - 统一返回结构(如
Result<t></t>),前端或上层根据 code 判断分支,不依赖 try-catch - 仅在真正异常场景(如 DB 连接中断、序列化失败)才抛出带栈的异常
禁用栈追踪或复用静态异常实例
对必须抛出但无需调试上下文的业务异常,可跳过栈采集;对固定原因的异常(如参数校验失败),避免重复创建。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 自定义异常类,重写
fillInStackTrace()返回this:
@Override
public Throwable fillInStackTrace() { return this; }
}
- 声明
private static final LightBizException DUPLICATE_ORDER = new LightBizException("order_already_exists");,直接复用,不 new - 注意:禁用栈后日志里看不到调用链,需靠 traceId + 业务字段定位问题
测量要准,别信均值,盯 P95 和火焰图
异常开销分布右偏,平均值会掩盖毛刺。实测时必须隔离干扰,落到真实链路节点。
- 计时点设在判断之后、throw 之前:
long start = System.nanoTime(); if (stock - 压测前执行
System.gc(),绑定 CPU 核,批量采样 1000 次取 P95 - 对比基线:同一逻辑用
Result.fail跑一次,差值即为异常链净开销 - 看火焰图确认
Throwable.fillInStackTrace是否出现在热点路径
日志与监控链路上关掉同步阻塞
Log4j2 默认异步 Appender 若没配无锁队列,或 SkyWalking 对异常做全量采样,都会让异常处理路径串行化。
- Log4j2 配置
AsyncLoggerConfig.RingBufferSize=65536并启用WaitStrategy=Timeout - 监控埋点只对 ERROR 级异常采样,且过滤掉已知业务码(如 stock_not_enough)
- 避免在 catch 块里做耗时操作:不拼接大字符串、不查 DB、不调远程服务
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










