
在高并发下,C# 异常处理的性能开销不是“有点大”,而是**单次异常抛出 ≈ 几百纳秒到数毫秒级延迟,且随调用栈深度指数上升**——这在每秒处理上万请求的系统里,足以让吞吐量断崖式下跌。
为什么一次 throw 就这么贵?
CLR 不是简单跳转,而是要:
- 遍历整个调用栈,查找匹配的
catch块(哪怕最终没找到) - 为每个栈帧生成完整堆栈跟踪(
StackTrace字符串、方法元数据、IL 偏移等) - 触发 JIT 临时编译异常处理相关代码路径(冷路径)
- 若未被立即捕获,还会多次重入异常分发逻辑(尤其跨 async 边界时)
实测:在 16 层调用栈 + Exception 类型下,throw new Exception() 平均耗时约 1.2–2.8 ms(.NET 6/7,Release 模式,中等负载 CPU)。这不是 CPU 计算开销,而是**不可缓存、不可预测、强阻塞的系统级开销**。
try/catch 本身几乎免费,但「进 catch 块」代价极高
很多人误以为 try 块有开销,其实不然:try 只是元数据标记,零运行时成本。真正昂贵的是——
-
catch (Exception ex)块被执行(意味着异常已展开完毕) - 更糟的是:
catch块里再throw或throw ex—— 这会重建堆栈,开销翻倍 - 在循环里写
try/catch(比如逐条解析 JSON),等于把毫秒级延迟放大 N 倍
for (int i = 0; i < 10000; i++)
{
try
{
ProcessItem(items[i]); // 若这里常失败 → 系统直接卡死
}
catch (FormatException) { /* ... */ }
}✅ 正确做法:用 int.TryParse、JsonSerializer.Deserialize<t>(ref Utf8JsonReader, ...)</t> 等无异常 API 替代控制流。
when 过滤器真能“短路”异常开销?
能,而且效果显著——它让 CLR 在**堆栈展开前就决定“不处理”**,跳过绝大部分昂贵步骤。
iWebShop基于iWebSI框架开发,在获得iWebSI技术平台库支持的条件下,iWebShop可以轻松满足用户量级百万至千万级的大型电子商务网站的性能要求。站点的集群与分布式技术(分布式计算与存储/高可用性/负载均衡)被屏蔽在SI 平台之内,基于iWebShop并且按照SI平台库扩展规范开发的新增功能模块,也将同时获得这种超级计算与处理的能力。作为开源的LAMP电子商务系统,iWebShop
对比两种写法:
// ❌ 传统方式:先展开,再判断,再丢弃
try { ... }
catch (IOException ex)
{
if (ex.HResult != -2147024864) throw; // 已经白花了 1ms+
HandleNetworkTimeout();
}
<p>// ✅ 异常过滤器:条件不满足 → 完全不进入 catch,不展开堆栈
try { ... }
catch (IOException ex) when (ex.HResult == -2147024864)
{
HandleNetworkTimeout();
}关键点:
-
when表达式在异常分发早期执行,不触发堆栈构建 - 表达式里禁止副作用(如修改状态、打日志),否则行为不可控
- 不能用于
catch (Exception)后无类型限定的场景(编译报错)
高频异常 = 隐形雪崩,监控比优化更优先
生产环境最危险的不是慢,而是“偶发慢”——比如数据库连接池耗尽时,每 1000 次请求抛一次 SqlException,QPS 看似正常,GC 耗时却悄悄涨了 300%。
必须做两件事:
- 用
dotnet-counters监控System.Runtime/Exceptions Per Second,阈值建议设为 > 10/s 即告警 - 在全局异常处理器(如 ASP.NET Core 的
UseExceptionHandler)里记录Exception.ToString()前,先检查exception.GetType().Name是否属于业务预期异常(如ValidationException),避免日志刷爆磁盘
真正难的不是写出高性能代码,而是意识到:**“这段逻辑本不该走异常路径”——那才是性能问题的根因。**










