ReadyToRun(R2R)是.NET 5+中通过dotnet publish启用的提前编译优化,需Release配置、指定runtime并设置PublishReadyToRun=true,生成平台绑定的含本地代码的.dll镜像,仍依赖.NET运行时。

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中添加true
示例命令:
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 不是开了就一定生效——编译成功不代表所有类型都被处理。容易被忽略的点:
- 未设置
且同时启用了 trimming(.NET 6+ 发布时默认 trim),会导致 R2R 被静默禁用(日志中出现false Skipping ReadyToRun compilation because trimming is enabled) - 引用了
Microsoft.NET.Sdk.BlazorWebAssembly等 SDK,会强制关闭 R2R(Blazor WebAssembly 不支持) - 验证是否生效:用
corflags(Windows)或readelf -S(Linux),看到| grep r2r R2R或.r2rdata段即表示已编译;也可用dotnet trace观察启动时是否有JITCompilationStarted事件大幅减少
R2R 的“镜像”本质是优化而非替代,它和 JIT 共存:未覆盖的方法、泛型实例化、动态生成代码仍走 JIT。别把它当成 Native AOT 的简易平替——该踩的兼容性坑,一个都不会少。










