Java 25 вийшла 16 вересня 2025 року, і більшість вендорів вважають її релізом із довгостроковою підтримкою (OpenJDK: JDK 25). Для enterprise-команд це сигнал планувати наступне оновлення платформи: саме LTS-версії роками працюють у продакшені. У статті зосереджуємося на тому, що реально змінюється для бекенд-сервісів, — обсяг пам'яті, старт і прогрівання, конкурентність, ергономіка мови, — і даємо практичний шлях оновлення з Java 17 чи 21, зокрема що перевірити в застосунках на Spring Boot.
Навіщо взагалі оновлюватися
Якщо ви на Java 17, ви пропустили два LTS-цикли покращень; якщо на 21 — стрибок менший, але все одно відчутний. Три причини, які ми чуємо від клієнтів найчастіше:
- Нижчі витрати на інфраструктуру. Пам'ять — головний чинник розміру контейнерів і віртуальних машин, а Java 25 робить compact object headers повноцінною функцією.
- Швидший старт і прогрівання. Важливо для автомасштабування, serverless і частих деплоїв у Kubernetes.
- Строки підтримки. Фреймворки посувають мінімальні версії. Spring Boot 4 залишає мінімальною Java 17, але додає повноцінну підтримку Java 25 (блог Spring, 2025), а бібліотеки дедалі частіше розраховують на новіші JDK.
Java 25 одним поглядом
JDK 25 містить 18 JEP. Фінальні (не preview) можливості, найважливіші для бекенд-команд:
| JEP | Можливість | Чому це важливо |
|---|---|---|
| 519 | Compact Object Headers | Менші об'єкти, менше heap, менше збирань сміття |
| 514 | Ahead-of-Time Command-Line Ergonomics | Простіше створення AOT-кешів для швидшого старту |
| 515 | Ahead-of-Time Method Profiling | Швидше прогрівання завдяки профілям із тренувального запуску |
| 521 | Generational Shenandoah | GC з короткими паузами і генераційною ефективністю |
| 506 | Scoped Values | Безпечніша і дешевша альтернатива ThreadLocal, ідеальна з віртуальними потоками |
| 513 | Flexible Constructor Bodies | Валідація та обчислення перед super(...) |
| 511 | Module Import Declarations | import module java.base; тощо |
| 512 | Compact Source Files and Instance Main Methods | Скрипти й невеликі утиліти без бойлерплейту |
| 510 | Key Derivation Function API | Стандартний API для KDF на кшталт HKDF |
Preview та incubator-можливості — structured concurrency (п'яте preview), stable values, примітивні типи в патернах, Vector API — варто пробувати, але preview API в продакшен-сервіси ми не беремо.
Пам'ять: compact object headers
Кожен Java-об'єкт має заголовок. JEP 519 робить compact object headers, що з'явилися експериментально в JDK 24, повноцінною функцією. Результати, наведені в JEP, суттєві: в одному сценарії бенчмарк SPECjbb2015 використав на 22% менше heap і на 8% менше CPU, а в іншому кількість збирань сміття скоротилася на 15% (JEP 519). JEP також зазначає, що цей формат заголовків уже використовували сотні продакшен-сервісів в Amazon.
За замовчуванням функцію не ввімкнено. Увімкніть явно і виміряйте:
java -XX:+UseCompactObjectHeaders -jar app.jar
Для сервісів із великою кількістю об'єктів — кешів, API з масою DTO, обробки подій — це одна з найдешевших оптимізацій: жодних змін у коді, а pod'и потенційно менші.
Старт і прогрівання: AOT-кеш
AOT-кеш із Project Leyden зберігає результати завантаження і лінкування класів, а з JDK 25 — ще й профілі методів із тренувального запуску. Наступний старт пропускає цю роботу, і JIT може оптимізувати одразу (JEP 514; JEP 515). У JDK 25 створення кешу скоротили до одного кроку:
# Тренувальний запуск: проганяємо застосунок, виходимо; JVM записує кеш
java -XX:AOTCacheOutput=app.aot -jar app.jar
# Продакшен-запуски використовують кеш
java -XX:AOTCache=app.aot -jar app.jar
Дві практичні примітки: тренувальний запуск має проганяти реалістичні шляхи коду (smoke-тест старту чи короткий навантажувальний тест), а однокроковий процес під час створення кешу потребує приблизно вдвічі більше пам'яті, ніж налаштований heap, як попереджає JEP. На відміну від нативних образів GraalVM, AOT-кеш зберігає повну JVM, JIT і динамічні можливості; підходи порівнюємо у статті GraalVM Native Image для Spring Boot.
Збирання сміття: generational Shenandoah
Генераційний режим Shenandoah став повноцінною функцією в JDK 25 (JEP 521). Він поєднує дуже короткі паузи Shenandoah з ефективністю окремого збирання молодих об'єктів. G1 залишається за замовчуванням і є правильним вибором для більшості сервісів; generational Shenandoah чи ZGC варто розглядати для чутливих до затримок сервісів із великим heap — і вирішувати за навантажувальними тестами, а не за бенчмарками з блогів.
Конкурентність: scoped values
Scoped values дозволяють методу передавати незмінні дані викликаним методам і дочірнім потокам із чітким часом життя (JEP 506). Вони дешевші й безпечніші за ThreadLocal, особливо з мільйонами віртуальних потоків, де thread-local змінні множать споживання пам'яті.
private static final ScopedValue<RequestContext> CONTEXT = ScopedValue.newInstance();
void handle(Request request) {
ScopedValue.where(CONTEXT, RequestContext.from(request))
.run(() -> orderService.process(request));
}
// будь-де нижче по стеку викликів, без передачі параметрів
RequestContext ctx = CONTEXT.get();
Самі віртуальні потоки з'явилися в Java 21; JDK 24 прибрав більшість випадків pinning у блоках synchronized, що зробило їх значно практичнішими. Про використання в продакшені — у статті Віртуальні потоки в Spring Boot.
Ергономіка мови
Гнучкі тіла конструкторів дозволяють інструкції перед super(...) чи this(...), якщо вони не звертаються до об'єкта, що конструюється (JEP 513). Валідація нарешті читається природно:
public class Money extends Amount {
public Money(BigDecimal value, Currency currency) {
if (value.signum() < 0) throw new IllegalArgumentException("Negative amount");
Objects.requireNonNull(currency);
super(value.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_EVEN));
this.currency = currency;
}
}
Імпорт модулів (import module java.sql;) і компактні вихідні файли з instance-методами main роблять написання невеликих утиліт і скриптів на Java приємним навіть без налаштування збірки (JEP 511; JEP 512).
План оновлення з Java 17 чи 21
- Інвентаризація. Перелічіть сервіси, їхні JDK, версії фреймворків та інструменти збірки. Перевірте, що Gradle, плагіни Maven, Lombok, Mockito, бібліотеки роботи з байт-кодом і агенти (APM, профайлери) підтримують Java 25.
- Спершу оновіть фреймворки. Перейдіть на Spring Boot 3.5 чи 4.x, Hibernate та інші залежності з офіційною підтримкою цільового JDK. Шлях Spring Boot 3.5 → 4 описано у статті Spring Boot + Kotlin у 2026.
- Зберіть новим JDK, прогоніть тести, виправте попередження. Типові проблеми між 17 і 25: видалені чи інкапсульовані внутрішні API, суворіші попередження про динамічне завантаження агентів і старі бібліотеки маніпуляції байт-кодом.
- Запустіть на staging під навантаженням. Порівняйте heap, CPU, паузи GC, старт і p99-затримку зі старою версією.
- Спробуйте безкоштовні виграші. Увімкніть compact object headers і AOT-кеш на одному сервісі та виміряйте.
- Розгортайте поступово, сервіс за сервісом, тримаючи старий образ для відкату.
- Оновіть базові образи і CI, щоб нові сервіси стартували на Java 25 за замовчуванням.
Проблеми оновлення, які трапляються найчастіше
| Проблема | Типовий симптом | Рішення |
|---|---|---|
| Старі бібліотеки байт-коду (ASM, Byte Buddy, cglib) | Помилки про непідтримувану версію class-файлу | Оновити бібліотеку чи фреймворк, що її містить |
| Lombok чи annotation processors | Помилки компіляції | Оновити до версії з підтримкою нового JDK |
| Динамічне завантаження агентів | Попередження під час старту від APM чи інструментів мокінгу | Завантажувати агентів явно через -javaagent |
| Видалені внутрішні API | IllegalAccessError, відсутні класи |
Замінити на публічні API; не використовувати --add-opens як постійне рішення |
| Налаштування пам'яті в контейнері | Розмір heap не такий, як очікувалося | Явно задати -XX:MaxRAMPercentage і виміряти знову |
FAQ
Java 25 справді LTS? Більшість вендорів, зокрема Oracle, Eclipse Adoptium, Amazon Corretto та Azul, надають для неї довгострокову підтримку. Тривалість залежить від вендора.
Чи варто пропустити Java 21 і перейти одразу з 17 на 25? Так, якщо ваші фреймворки це підтримують. Зусилля на міграцію з 17 на 25 ненабагато більші, ніж на 21, а років підтримки ви отримуєте більше.
Чи робить Java 25 непотрібним GraalVM native image? Не повністю. AOT-кеш суттєво покращує старт, зберігаючи повну JVM; нативні образи все одно стартують швидше і споживають менше пам'яті, але обмежують динамічні можливості.
Чи можуть compact object headers щось зламати? Для Java-коду вони прозорі. Протестуйте нативні бібліотеки та інструменти, що аналізують розміщення об'єктів у пам'яті, і виміряйте ефект, перш ніж вмикати всюди.
Джерела
- OpenJDK. Сторінка проєкту JDK 25.
- OpenJDK. JEP 519: Compact Object Headers.
- OpenJDK. JEP 514: AOT Command-Line Ergonomics і JEP 515: AOT Method Profiling.
- OpenJDK. JEP 521: Generational Shenandoah.
- OpenJDK. JEP 506: Scoped Values, JEP 513: Flexible Constructor Bodies, JEP 511: Module Import Declarations, JEP 512: Compact Source Files.
- Spring (2025). Spring Boot 4.0.0 available now.