引子:为什么你的 API 延迟总是比别人慢 10 ms?
同样的 8 核 16 G 云主机、同样的数据库、同样的业务逻辑,别人的 P99 延迟 12 ms,你的却 22 ms。多出来的 10 ms,往往藏在 JIT 编译、GC 停顿、锁竞争、CPU 缓存失效、线程调度五个黑洞里。本篇用一万字带你走进 .NET 性能宇宙的每一道裂缝。
第一章:运行时地基——CLR、CoreCLR、Mono、Native AOT 的四国演义
1.1 CLR 的历史包袱:Server GC、Concurrent GC、Background GC 的进化
1.2 CoreCLR 的跨平台之路:RyuJIT、ReadyToRun、Crossgen2
1.3 Mono 的 AOT 传统:嵌入游戏引擎、Unity、Xamarin
1.4 Native AOT 的涅槃:dotnet/publishAOT、单文件、启动时间 1/10
第二章:JIT 编译器 360° 解剖
2.1 Tiered Compilation:从 Quick JIT 到 Optimized JIT 的分层策略
2.2 Profile-Guided Optimization (PGO):运行时采样 vs. 静态分析
2.3 Dynamic PGO:OSR(On-Stack Replacement)让热点循环在飞行中升级
2.4 Loop Vectorization:SIMD 指令自动注入的魔法与陷阱
2.5 冷启动优化:Crossgen2 + ReadyToRun 的预编译艺术
第三章:垃圾回收宇宙
3.1 三代 GC 算法:标记-清除、标记-压缩、标记-复制
3.2 Server vs. Workstation:吞吐优先还是延迟优先
3.3 Concurrent & Background:把 STW 切成碎片
3.4 LOH 压缩:大对象堆不再碎片化
3.5 Pinning 与 Span<T>
第四章:并发原语与锁战争
4.1 Monitor、lock、SpinLock、ReaderWriterLockSlim 的选型地图
4.2 Interlocked 家族:CAS、CompareExchange、MemoryBarrier
4.3 Channel<T>
第五章:集合与数据结构
5.1 List<T><T><T><T>
第六章:异步与线程池
6.1 ThreadPool 2.0:IO 完成端口与 Hill Climbing 算法
6.2 ValueTask vs. Task:同步路径零分配
6.3 async/await 状态机:编译器生成的 switch 表
6.4 ConfigureAwait(false) 的宗教战争
6.5 限流与背压:SemaphoreSlim、Channel<T>
第七章:数据库与网络
7.1 ADO.NET 池化:连接、命令、读取器三级缓存
7.2 EF Core 追踪与无追踪:快照变更检测的代价
7.3 Dapper AOT:表达式树 vs. 源生成器
7.4 HttpClient 连接池:SocketsHttpHandler 的 DNS 刷新策略
7.5 gRPC:HTTP/2 多路复用与 HPACK 压缩
第八章:Native AOT 与单文件部署
8.1 ILC 编译器:IL → RyuJIT → 机器码的离线流程
8.2 Trimming:ILLinker 的保守剪裁与警告分类
8.3 Reflection-free 模式:源生成器替代运行时反射
8.4 启动时间与内存对比:冷启动 300 ms → 30 ms 的案例
8.5 体积瘦身:Chiseled Ubuntu + ReadyToRun 复合策略
第九章:诊断与观测
9.1 EventSource + EventPipe:运行时事件的统一通道
9.2 dotnet-trace + speedscope:火焰图定位热点
9.3 dotnet-dump + SOS:托管堆调试的瑞士军刀
9.4 PerfView:Windows 平台下的终极利器
9.5 eBPF + bpftrace:容器内系统调用追踪
第十章:实战案例集锦
10.1 某电商秒杀:通过 ArrayPool 把 GC 次数降低 90%
10.2 某金融风控:Native AOT 把容器冷启动从 5 s 降到 600 ms
10.3 某短视频推荐:Span<T><T>
结语:性能调优没有银弹
引用 CLR 性能工程师 Maoni Stephens 的话:“性能是特性之一,而非补丁。” 只有深入理解运行时、编译器、库、网络、存储的每一层,才能把那 10 ms 的差距追回来。