Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top

Вебинар: Можно ли быть тимлидом, но не быть экспертом? Или тренеры не играют - 25.09

>
>
>
Нововведения Java 27

Нововведения Java 27

15 Сен 2026
Автор:

Java 27 вышла в релиз. В него вошли четыре узконаправленные правки и пять превью/инкубаторов. Разбираем изменения в сборщике мусора, улучшения JFR, постквантовый TLS 1.3 и компактные заголовки объектов, экономящие треть памяти.

Изменения в платформе Java

JEP 534: Compact Object Headers by Default

Ссылка на JEP

Помните, как в позапрошлый раз, в статье про Java 25, мы упоминали сжатые заголовки объектов, которые включаются флагом? Так вот, флаг теперь лишний.

Тогда мы прошлись по этой теме совсем вскользь. Сейчас же, поскольку теперь это идёт по умолчанию, разберёмся поподробнее.

Каждый объект в куче начинается со служебного заголовка. В классической раскладке (в большинстве JVM) это два блока:

  • состояние блокировки, возраст объекта для GC, identity hash;
  • сжатый указатель на метаданные класса.

В сумме получается 96 бит (12 байт) на каждый объект, даже если полезных данных в нём всего ничего. Compact Object Headers ужимают всё это в 64 бита: указатель на метаданные класса сжимается до 22.

Теоретически сэкономленные биты — это, конечно, хорошо, но что мы как пользователи JRE получим на практике? Сейчас проверим.

Не так давно у нас вышел в релиз JavaScript/TypeScript анализатор, разработанный на Java 25. Возьмём его в качестве подопытного и натравим на исходный код Grafana. Запустим его с Compact Object Headers и без.

Через три минуты у нас появляется табличка:

Метрика

Без флага

С флагом

Время

81.4 сек

79.4 сек

Committed heap

1 290 240K (~1.23 ГБ)

1 126 400K (~1.07 ГБ)

Used heap на выходе

1 150 092K (~1.1 ГБ)

773 062K (~738 МБ)

Commited heap — сколько памяти забрала виртуальная машина у ОС и держит зарезервированной. Used heap — использование памяти в моменте, то есть сколько байт занимают живые объекты, ещё не удалённые сборщиком мусора.

Как итог, время анализа в рамках погрешности почти не изменилось, committed heap упал на 13%, а used heap стал на целых 33% меньше! Всего один флаг понизил потребление памяти нашим приложением на целую треть!

Плата за это счастье: 22 битный указатель ограничивает количество загруженных классов примерно четырьмя миллионами. Автоматического отката на некомпактный режим нет — при превышении получите OutOfMemoryError. Если ваше приложение действительно упирается в этот предел, вы, наверное, очень любите долгоживущие стримы. Ну а вернуть как было можно другим флажком:

java -XX:-UseCompactObjectHeaders Application

JEP 523: Make G1 the Default Garbage Collector in All Environments

Ссылка на JEP

До Java 27 JVM смотрела на машину при старте и, если видела меньше двух процессоров или меньше 1792 МБ памяти, молча выдавала Serial GC вместо G1. Такая машина считалась "клиентской", и использовался наиболее подходящий под неё сборщик памяти.

А теперь угадайте, как выглядит скромный контейнер для JVM с лимитом в один CPU и 512 МБ памяти?

Правильно. Часть контейнеризованной Java всё это время ездила на сборщике, ориентированном на клиентское окружение, причём никто такого решения сознательно не принимал. Хуже того, одно и то же приложение могло получить разные сборщики мусора на сервере и у разработчика в рабочей среде, что превращало воспроизведение проблем с паузами в увлекательный квест с нотками шизофренического смеха.

В Java 27 решили на окружение не смотреть — G1 выбирается всегда:

# Java 25
$ java -XX:ActiveProcessorCount=1 -Xmx256m -Xlog:gc --version
[0.001s][info][gc] Using Serial
 
# Java 27
$ java -XX:ActiveProcessorCount=1 -Xmx256m -Xlog:gc --version
[0.001s][info][gc] Using G1

Обоснование простое: улучшения пропускной способности G1 в прошлых релизах свели отставание от Serial почти к нулю, а паузы у G1 заметно приятнее, потому что он умеет работать конкурентно и параллельно. Плюс предсказуемость: поведение приложения больше не зависит от того, сколько ядер увидела JVM при старте.

Если Serial нужен осознанно — у него всё ещё наименьшие накладные расходы по памяти и быстрее старт, — он никуда не делся: -XX:+UseSerialGC.

Безопасность

JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3

Ссылка на JEP

Вы слышали что-то про "harvest now, decrypt later"?

Забавная категория угроз, где злоумышленник, имея доступ к вашему трафику, но не имея возможности вскрыть TLS-соединение, просто сохраняет себе зашифрованный трафик на потом.

Зачем ему это? Злоумышленник верит в мировой прогресс и ждёт квантовый компьютер, способный сломать обмен ключами на эллиптических кривых. Медицинские данные, банковская переписка, ключи и персональные данные останутся ценными и через десять лет, так что схема вполне рабочая.

Вслед за HTTP/3 (см. предыдущую статью) появляется поддержка TLS 1.3. Ключ сессии в TLS 1.3 теперь вырабатывается двумя алгоритмами сразу: тем самым классическим обменом на эллиптических кривых и квантово-устойчивым алгоритмом. Такая пара называется гибридной группой, и смысл её в том, что соединение остаётся защищённым, пока держится хотя бы один из двух алгоритмов. Ждать квантовый компьютер становится бессмысленно.

Для приложения это бесплатно в смысле кода: javax.net.ssl сам ставит гибридную группу первой в списке предпочтений. Если собеседник её поддерживает, обмен идёт гибридным путём; если нет, то всё работает как раньше. Трогать ничего не нужно, если только вы не задавали список групп руками через jdk.tls.namedGroups или SSLParameters::setNamedGroups.

Но кое-где всё же придётся немного заплатить: рукопожатие становится примерно на два килобайта дороже. Для большинства приложений это незаметно, но при миллионах коротких соединений в трафике вы это увидите.

Важная оговорка: JEP касается только обмена ключами. Подписи сертификатов пока остаются классическими, и это осознанное решение: подделать подпись задним числом нельзя, а расшифровать записанный трафик очень даже можно.

Улучшения JFR

JEP 536: JFR In-Process Data Redaction

Ссылка на JEP

JFR при записи заботливо сохраняет всё окружение процесса: переменные окружения, системные свойства и аргументы командной строки. А там, как известно, живут пароли к БД, токены и ключи от всего на свете. Поэтому отправка .jfr-файла коллеге — а тем более прикрепление его к публичному issue — всегда сопровождалась лёгкой тревогой. В большинстве случаев .jfr вообще не стоит выкладывать в общий доступ без предварительной чистки.

Начиная с Java 27, JFR затирает чувствительные значения внутри JVM до того, как они попадут на диск. А что JFR считает чувствительными данными? По умолчанию любая переменная окружения или системное свойство, имя которого попадает под один из шаблонов, получает значение [REDACTED]:

*api*key*      *auth*        *client*secret*  *credential*
*jaas*config*  *passphrase*  *passwd*         *password*
*private*key*  *pwd*         *secret*         *token*

Аргументы командной строки вида --db-password qwerty123 обрабатываются так же.

Расширить список свойств можно с помощью флага -XX:FlightRecorderOptions:redact-key=+<your-pattern>.

Важно понимать границы: механизм покрывает стартовые события (переменные окружения, системные свойства, аргументы, информацию о JVM) и честно объявлен best-effort. Он не чистит события вашего приложения, сообщения исключений и командные строки дочерних процессов. То есть .jfr стал заметно безопаснее, но не стерильным. Продолжаем волноваться, но уже не так сильно.

In Preview

В качестве предварительного просмотра в релиз включили ещё несколько JEP. Рассматривать их сейчас не будем, дождёмся готовых версий. Вот ссылки для самостоятельного ознакомления:

Заключение

Полный список JEP можно найти на сайте OpenJDK.

Релиз получился маленький, и ничего переворачивающего разработку на 180 градусов тут не стоило и ждать: обновили превью, сделали несколько изменений в платформу. Зато теперь JVM делает больше нужных вещей из коробки: объекты в куче ужимаются, сборщик мусора один и тот же для сервера и для крошечного пода, записи JFR перестают сиять секретами, а TLS готовится к квантовому будущему.

По сути, это подготовка к следующему LTS. Structured Concurrency доходит до седьмого превью, примитивы в паттернах до пятого, Vector API ждёт Valhalla. Всё это доваривается, чтобы к Java 29 можно было подавать в готовом виде. Ну а пока просто обновляемся и получаем более компактную кучу бесплатно.

Подписаться на рассылку
Хотите раз в месяц получать от нас подборку вышедших в этот период самых интересных статей и новостей? Подписывайтесь!
Популярные статьи по теме

Комментарии (0)

Следующие комментарии next comments
close comment form