C# ReadyToRun镜像编译方法 C# R2R和Native AOT有什么不同

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

ReadyToRun 编译怎么开

ReadyToRun(R2R)是 .NET 5+ 中对 IL 程序集做的提前编译优化,生成平台相关的机器码镜像,运行时可跳过 JIT 编译阶段。它不是独立构建模式,而是

dotnet publish
的一个选项:

必须用
Release
配置,Debug 下默认禁用
需指定
--runtime
(如
win-x64
linux-arm64
),R2R 镜像是平台绑定的
启用方式:加
--no-self-contained false
(默认)或
--self-contained true
,再加
--crossgen2
参数(.NET 6+ 默认启用
crossgen2
显式开启 R2R:在
.csproj
中添加
<publishreadytorun>true</publishreadytorun>

示例命令:

dotnet publish -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=true

生成的输出目录里,

.dll
文件会被
crossgen2
处理成含本地代码的“R2R 镜像”,文件大小明显增大,且
dotnet --list-runtimes
不影响其运行(它仍依赖 .NET 运行时)。

R2R 和 Native AOT 的根本区别

R2R 是“带本地代码的托管程序”,Native AOT 是“彻底脱离运行时的原生二进制”。二者目标相似(启动快、内存省),但技术路径和约束完全不同:

ReadyToRun
仍依赖完整 .NET 运行时(
libcoreclr.so
coreclr.dll
),只是把部分方法提前编译了;
nativeaot
输出的是纯原生可执行文件,不带任何托管运行时组件
R2R 支持反射、动态加载(
Assembly.Load
)、
eval
类 API(如
Expression.Compile
);Native AOT 在编译期就要确定所有可达代码,反射需用
[DynamicDependency]
TrimmerRootDescriptor
显式声明
R2R 编译快、兼容性高,几乎不用改代码;Native AOT 构建慢、限制多,常见报错如
ILLink failed: 'System.Reflection.MissingMetadataException'
,意味着某处反射没被正确标注
R2R 输出仍是
.dll
(含嵌入本地代码),靠运行时加载;Native AOT 输出是
.exe
(Windows)或无扩展名可执行文件(Linux/macOS)

什么时候该选 R2R 而不是 Native AOT

如果你的应用需要以下任一能力,R2R 是更现实的选择:

使用第三方 NuGet 包(尤其是含大量反射或运行时代码生成的库,如
Newtonsoft.Json
EntityFrameworkCore
AutoMapper
依赖插件机制(
AssemblyLoadContext.LoadFromAssemblyPath
调用
System.Text.Json.SourceGeneration
以外的源生成器(R2R 兼容所有源生成器)
部署环境无法预装 .NET 运行时,但允许你打包自包含运行时(R2R +
--self-contained true
即可)

Native AOT 只适合“可控边界清晰”的场景:CLI 工具、云函数、嵌入式服务端逻辑,且你愿意为每个反射调用加

[UnconditionalSuppressMessage]
或重写逻辑。

R2R 常见失效原因和验证方法

R2R 不是开了就一定生效——编译成功不代表所有类型都被处理。容易被忽略的点:

未设置
<publishtrimmed>false</publishtrimmed>
且同时启用了 trimming(.NET 6+ 发布时默认 trim),会导致 R2R 被静默禁用(日志中出现
Skipping ReadyToRun compilation because trimming is enabled
引用了
Microsoft.NET.Sdk.BlazorWebAssembly
等 SDK,会强制关闭 R2R(Blazor WebAssembly 不支持)
验证是否生效:用
corflags <your.dll></your.dll>
(Windows)或
readelf -S <your.dll> | grep r2r</your.dll>
(Linux),看到
R2R
.r2rdata
段即表示已编译;也可用
dotnet trace
观察启动时是否有
JITCompilationStarted
事件大幅减少

R2R 的“镜像”本质是优化而非替代,它和 JIT 共存:未覆盖的方法、泛型实例化、动态生成代码仍走 JIT。别把它当成 Native AOT 的简易平替——该踩的兼容性坑,一个都不会少。

相关推荐