Сервіс на Spring Boot, що стартує за 50 мілісекунд і працює в 80 МБ пам'яті, звучить як зовсім інша платформа, ніж Java, яку знає більшість команд. GraalVM Native Image робить це можливим, заздалегідь компілюючи застосунок в автономний виконуваний файл. Водночас він змінює підхід до збірки, тестування й налагодження, а у 2025 році Oracle змінила роль GraalVM в екосистемі Java. У гайді пояснюємо, як нативні образи працюють зі Spring Boot, де вони окупаються, чого коштують і коли простіша відповідь — власний AOT-кеш JDK з Project Leyden.
Як працює native image
Звичайний Java-застосунок запускає JVM, завантажує класи, інтерпретує байт-код і поступово компілює гарячий код JIT-компілятором. Нативний образ виконує цю роботу під час збірки: GraalVM аналізує застосунок, починаючи з main, включає лише досяжний код, ініціалізує частину з нього і створює виконуваний файл під конкретну платформу (GraalVM: Native Image).
Аналіз спирається на припущення закритого світу: усе, що застосунок може колись виконати, має бути відоме під час збірки. Reflection, динамічні проксі, ресурси та серіалізація мають або визначатися статичним аналізом, або оголошуватися через метадані («hints»).
Як це підтримує Spring Boot
Spring Boot має повноцінну підтримку нативних образів через Spring AOT (Spring Boot: GraalVM native images). Під час збірки Spring AOT обчислює контекст застосунку, генерує вихідний код для визначень бінів і створює підказки reflection та ресурсів, потрібні GraalVM. Більшість проєктів Spring і багато популярних бібліотек уже постачаються з підказками.
Збірка — це задача плагіна:
# Gradle із плагіном GraalVM Native Build Tools
./gradlew nativeCompile
./build/native/nativeCompile/orders-service
# Maven
./mvnw -Pnative native:compile
# Або контейнерний образ через Cloud Native Buildpacks, без локального GraalVM
./gradlew bootBuildImage # з увімкненим buildpack для native image
Що ви виграєте
- Старт за десятки мілісекунд замість секунд — різниця між тим, чи масштабування до нуля практичне, чи ні.
- Менше споживання пам'яті — часто частка від еквівалентного JVM-процесу, а отже менші контейнери і щільніші ноди.
- Без прогрівання: пікова продуктивність доступна одразу, що важливо для короткоживучих задач і функцій.
- Менша поверхня атаки: у бінарнику лише досяжний код.
Чим ви платите
- Час і ресурси збірки. Нативна компіляція типового сервісу на Spring Boot займає хвилини й кілька гігабайтів RAM замість секунд для JAR. CI має це враховувати; див. CI/CD без болю.
- Конфігурація фіксується під час збірки.
@Profileі@ConditionalOnPropertyу Spring обчислюються на етапі AOT, тож набір бінів не може змінюватися в рантаймі, як на JVM. - Динамічні можливості потребують підказок. Бібліотеки, що використовують reflection, динамічне завантаження класів чи генерацію байт-коду в рантаймі, можуть потребувати додаткової конфігурації або не працювати зовсім.
- Пікова пропускна здатність може бути нижчою, ніж у повністю прогрітого JIT, для довготривалих сервісів із важким навантаженням на CPU, якщо не використовувати profile-guided optimization.
- Інше налагодження і профілювання. Звичні інструменти JVM на кшталт JFR доступні лише частково; для нативного налагодження потрібні інші інструменти.
- Бінарники під платформу. Збирати доводиться під кожну ОС і архітектуру CPU.
Власні підказки для вашого коду з reflection виглядають так:
@Configuration
@ImportRuntimeHints(ReportHints.class)
class ReportConfig { }
class ReportHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(ReportRow.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
hints.resources().registerPattern("reports/*.jrxml");
}
}
Тестуйте саме нативний бінарник, а не лише JVM-збірку: Spring Boot уміє запускати тести в нативному режимі (./gradlew nativeTest), що ловить відсутні підказки до продакшену.
Зміни GraalVM від Oracle у 2025 році
У вересні 2025 року Oracle оголосила, що команда GraalVM зосередиться на мовах, відмінних від Java, як-от GraalPy і GraalJS, що GraalVM for JDK 24 — останній реліз, ліцензований і підтримуваний у складі продуктів Oracle Java SE, а цілі швидшого старту, прогрівання і меншого обсягу пам'яті для Java переходять до Project Leyden в OpenJDK (InfoWorld, 2025).
Що це означає на практиці:
- GraalVM 25, зокрема Native Image, вийшла, Community Edition залишається доступною, а Spring Boot і далі підтримує нативні образи.
- Комерційна підтримка native image у складі підписки Oracle Java SE припинилася; інші дистрибутиви та вендори надають власні збірки й підтримку.
- Довгостроковий напрям у стандартній Java щодо старту й пам'яті — Project Leyden.
Для нових проєктів це змінює запитання за замовчуванням із «чи переходити на native?» на «чи вистачить AOT-кешу Leyden?».
Альтернатива: AOT-кеш JDK 25
Починаючи з JDK 24, JVM може створити ahead-of-time кеш із тренувального запуску, що зберігає завантажені й злінковані класи, а з JDK 25 — ще й профілі методів, тож наступний старт пропускає цю роботу, а JIT оптимізує одразу (JEP 514; JEP 515). Ви зберігаєте повну JVM — усі динамічні можливості, JFR, звичні інструменти — і отримуєте значну частину прискорення старту зміною одного рядка. Детальніше — у статті Java 25 LTS: що нового.
| Критерій | JVM | JVM + AOT-кеш (JDK 25) | Native image |
|---|---|---|---|
| Старт | Секунди | Помітно швидше | Десятки мілісекунд |
| Обсяг пам'яті | Найбільший | Як у JVM | Найменший |
| Пікова пропускна здатність | Найкраща після прогрівання | Найкраща, досягається швидше | Добра; PGO допомагає |
| Динамічні можливості | Усі | Усі | Потрібні підказки, частина не підтримується |
| Складність збірки | Проста | Тренувальний запуск у CI | Довгі збірки, під кожну платформу |
| Інструменти | Повні | Повні | Часткові |
Коли native image — правильний вибір
- Масштабування до нуля і serverless: функції та сервіси, що стартують на вимогу.
- CLI-утиліти, які поширюються користувачам чи розробникам.
- Дуже щільні розгортання, де вартість визначає пам'ять на інстанс, — наприклад, багато невеликих сервісів на ноду.
- Короткоживучі пакетні задачі, яким ніколи не допомагає прогрівання JIT.
Коли залишатися на JVM
- Довготривалі сервіси зі стабільним трафіком, де старт трапляється рідко, а пікова пропускна здатність важлива.
- Застосунки на дуже динамічних бібліотеках чи фреймворках.
- Команди без ресурсу на підтримку підказок, нативних тестів і довших збірок.
Для них JVM з віртуальними потоками й AOT-кешем зазвичай кращий компроміс; див. Віртуальні потоки в Spring Boot.
Чек-лист для рішення
Дайте відповіді на ці запитання, перш ніж переводити сервіс на native image:
- Чи стартує сервіс достатньо часто — автомасштабування, масштабування до нуля, короткі задачі, — щоб час старту мав значення?
- Чи є пам'ять на інстанс суттєвою частиною рахунку за інфраструктуру?
- Чи всі ключові бібліотеки підтримують native image або постачаються з метаданими досяжності?
- Чи може CI дозволити собі нативні збірки по кілька хвилин і кілька гігабайтів RAM на збірку?
- Чи готова команда запускати тести в нативному режимі й налагоджувати специфічні для native проблеми?
Якщо більшість відповідей — «ні», почніть із JVM і AOT-кешу та поверніться до питання пізніше.
Поради щодо CI для нативних збірок
- Збирайте нативно лише там, де це важливо. Звичайні JVM-тести — на кожен pull request, нативні збірки плюс
nativeTest— на головній гілці або щоночі. - Агресивно кешуйте залежності та збірку Gradle чи Maven; сама нативна компіляція не інкрементна.
- Використовуйте потужніші runner'и для нативних джоб або self-hosted runners на власному кластері, як описано у статті CI/CD без болю.
- Збирайте під кожну архітектуру. Створюйте бінарники amd64 і arm64 в окремих джобах і публікуйте мультиархітектурний образ.
- Відстежуйте розмір бінарника й час старту як метрики в CI, щоб регресії були видимими.
FAQ
Чи працює native image зі Spring Data JPA та Hibernate? Так, Spring Boot і Hibernate надають потрібні підказки для типових конфігурацій. Тестуйте свої запити й мапінги в нативному режимі.
Чи можна використовувати Kotlin? Так. Нативна підтримка Spring Boot охоплює застосунки на Kotlin.
Чи застарів GraalVM Native Image? Ні. GraalVM 25 постачається з Native Image, і Spring Boot його підтримує, але Oracle більше не включає його в підписку Java SE і вказує на Project Leyden як на шлях стандартної Java.
Скільки пам'яті споживає нативний сервіс на Spring Boot? Залежить від застосунку; невеликі сервіси часто працюють у десятках мегабайтів. Міряйте власний під навантаженням, наближеним до продакшену.
Чи можна поєднувати нативні й JVM-сервіси в одній системі? Так, і це поширена практика. Використовуйте нативні образи там, де старт і пам'ять найважливіші, — для воркерів на вимогу й функцій, — а решту тримайте на JVM з AOT-кешем. Обидва варіанти мають однакові HTTP- і messaging-інтерфейси.
Чи покращує native image безпеку? Він зменшує поверхню атаки, бо недосяжний код і JIT не включаються, але не замінює сканування залежностей і оновлення.
Джерела
- GraalVM. Native Image reference manual.
- Spring Boot. Introducing GraalVM native images.
- InfoWorld (2025). GraalVM 25 arrives, backed by JDK 25.
- OpenJDK. JEP 483: Ahead-of-Time Class Loading & Linking, JEP 514, JEP 515.