Перейти к основному содержимому

Native AOT в .NET

Разработчику Архитектору

Native AOT (Ahead-of-Time) — режим публикации, при котором приложение компилируется в нативный исполняемый файл до запуска. Обычный .NET при старте использует JIT (Just-In-Time) — компилятор превращает IL в машинный код уже во время работы программы.

Обзор цепочки IL → CLR → JIT — Платформа .NET, архитектура. Таблица версий — 48. Сборка и деплой — 14.


Словарь​

ТерминЗначение
IL (Intermediate Language)Промежуточный байт-код .NET после компиляции C#.
JITКомпиляция IL в машинный код при запуске.
AOTКомпиляция в машинный код до запуска.
TrimmingУдаление неиспользуемого кода из сборки при публикации.
РефлексияЧтение метаданных типов в runtime (typeof, GetProperty).
Cold startВремя от старта процесса до первого полезного ответа.

Когда Native AOT уместен​

СценарийAOT
CLI-утилита, cron, sidecarОбычно да
Serverless с жёстким cold startОбычно да
IoT, контейнер с малым RAMОбычно да
Крупный ASP.NET + EF + SwaggerЧасто нет на текущем этапе
Плагины из DLL в runtimeНет

ReadyToRun (R2R) — промежуточный вариант: IL + частично готовый нативный код, JIT остаётся. Сравнение — 14.


Минимальный проект​

Файл MyTool.csproj:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
</Project>

Публикация:

dotnet publish -c Release -r linux-x64 --self-contained

На выходе — бинарник, который можно запустить без установленного .NET на машине (self-contained).

InvariantGlobalization уменьшает размер, если приложению не нужны локали всех культур.


Ограничения​

ВозможностьВ Native AOT
Reflection.Emit, динамические сборкиНедоступно
Рефлексия без аннотацийМожет быть вырезана trimmer
Newtonsoft.Json без source generatorЧасто проблемно
Expression.Compile()Ограничено
Загрузка плагинов из произвольных DLLПрактически нет

Что помогает

  • Source generators для JSON (System.Text.Json + JsonSerializerContext)
  • Явные контракты DI без рефлексии "по имени"
  • Минимум динамической генерации кода

Trimming и анализаторы​

При AOT и trim анализатор предупреждает о коде, который зависит от рефлексии, но не помечен для сохранения.

  • DynamicallyAccessedMembers — подсказка компилятору, какие члены типа нужны.
  • RequiresUnreferencedCode — пометка API, несовместимого с trimming.

Тестируйте опубликованный артефакт (dotnet publish), а не только dotnet run в Debug — ошибки часто проявляются только после trim.


ASP.NET Core и AOT​

С .NET 8+ поддержка AOT для части стека ASP.NET расширяется, но не всё из экосистемы NuGet совместимо.

Перед выбором AOT для веб-приложения:

  1. Прочитайте документацию Microsoft для вашей версии SDK.
  2. Проверьте зависимости на совместимость.
  3. Для типичного LOB API с EF чаще достаточно обычного publish в контейнер — контейнеризация.

CLI и edge-сценарии — выбор архитектуры.


Схема выбора​


Частые ошибки​

  • Тест только в Debug — AOT-ошибки в Release/publish.
  • Игнорирование trim warnings — MissingMethodException в продакшене.
  • AOT без измеримой выгоды — лишняя сложность разработки.

Краткая шпаргалка​

ЦельДействие
Включить AOT<PublishAot>true</PublishAot>
Self-containeddotnet publish -r linux-x64 --self-contained
Меньше globalization<InvariantGlobalization>true</InvariantGlobalization>
JSON без рефлексииJsonSerializerContext (source generator)

См. также​