c# aot 是什么 .net aot 性能如何

来源:这里教程网 时间:2026-02-21 17:38:04 作者:

C# AOT 不是“另一种编译方式”,而是 .NET 应用从“带运行时启动”走向“像 Go 一样秒开”的关键切换点——它把 IL 中间语言在发布时就彻底翻译成目标平台的原生机器码,跳过运行时 JIT 编译环节。

dotnet publish -r win-x64 --self-contained 和 dotnet publish -c Release /p:PublishAot=true 有啥区别?

前者是传统“自包含部署”,仍含完整 .NET 运行时(约 100MB+),启动时仍要 JIT;后者才是真 AOT:生成纯原生可执行文件,无 .NET 运行时依赖,体积通常压缩到 15–40MB(取决于代码量和 trimming 程度)。

PublishAot=true
必须配合
RuntimeIdentifier
(如
win-x64
linux-arm64
)使用,否则构建失败
不加
--self-contained
时,AOT 模式默认就是自包含的——这个参数在 AOT 下已冗余
若项目含
Microsoft.Extensions.Hosting
,需显式禁用某些动态特性,否则
ilc
编译器会报错:
ILC : error IL2026: Using member '...' which has 'RequiresUnreferencedCode' attribute

为什么加了
PublishAot=true
却没变快?常见失效场景

AOT 效果被“反射”“序列化”“动态委托”等行为悄悄抵消——只要代码里有

typeof(T).GetMethod(...)
JsonSerializer.Serialize(obj)
(未配置源生成)、
Assembly.LoadFrom
,AOT 编译器就无法静态确定调用路径,要么报错,要么自动回退到部分 JIT 模式(即“混合模式”,性能提升打折)。

JSON 序列化必须用源生成器:
<packagereference include="System.Text.Json.SourceGeneration" version="8.0.0"></packagereference>
,并配置
JsonSerializerContext
反射调用需标注
[RequiresUnreferencedCode]
并用
<trimmerrootassembly></trimmerrootassembly>
<ilcarg>--reflection-serialization</ilcarg>
显式保留
泛型集合(如
List<mytype></mytype>
)若类型未在编译期“被看到”,AOT 会丢弃其实现,导致运行时报
MissingMethodException

.NET 9 AOT 实测性能:启动快多少?内存省多少?

以一个最小 Web API(仅返回

"OK"
)为例,在 Linux x64 容器中冷启动实测:

dotnet run             # JIT 模式:启动耗时 ~850ms,内存峰值 ~120MB
dotnet publish -r linux-x64 -c Release /p:PublishAot=true
./bin/Release/net9.0/linux-x64/publish/myapp  # AOT 模式:启动耗时 ~120ms,内存峰值 ~65MB

也就是说,启动时间缩短近 85%,内存占用减半。但注意:长期运行的吞吐量(如 QPS)未必更高——JIT 能根据运行时热点做动态优化,AOT 是“一次编译,终身不变”。对 Serverless、CLI 工具、桌面小应用,这是质变;对跑几天不重启的后端服务,收益集中在首请求延迟。

真正容易被忽略的是:AOT 不是开关一开就完事,它把“运行时不确定性”提前到了构建期。你得让编译器“看懂”所有可能路径,否则不是慢,而是直接起不来。

相关推荐