JDK 22 — Новые фичи и перспективы
Январь 30, 2024
18 января версия JDK 22 вошла в Rampdown Phase Two: это значит, что перечень JEP, которые будут включены в этой релиз, финализирован. Хотя JDK 22 — это не версия с долгосрочной поддержкой, полезно знать направление развития платформы и заранее продумать миграцию на LTS-версию. Поэтому давайте подробнее рассмотрим новые и улучшенные фичи предстоящего релиза.
- JEP 423: Блокировка областей памяти для G1
- JEP 447: Выражения перед super(…) (Preview)
- JEP 454: Foreign Function & Memory API
- JEP 456: Безымянные паттерны и переменные
- JEP 457: Class-File API (Preview)
- JEP 458: Запуск приложений, состоящих из множества файлов исходного кода
- JEP 459: Строковые шаблоны (второй Preview)
- JEP 460: Vector API (седьмой Incubator)
- JEP 461: Stream Gatherers (Preview)
- JEP 462: Структурная многопоточность (второй Preview)
- JEP 463: Неявно объявленные классы и main методы экземпляра класса (второй Preview)
- JEP 464: Scoped Values (второй Preview)
- Переходите на Axiom JDK Pro с поддержкой любой версии Java
JEP 423: Блокировка областей памяти для G1
JEP 423 направлен на улучшение работы сборщика мусора G1, который больше не будет отключаться при наличии критических областей Java Native Interface (JNI). Это позволит сократить время отклика во время сборки мусора.
JNI использует указатели на соответствующие Java-объекты для работы с языками программирования без автоматического управления памятью. Области памяти, в которых находятся эти объекты, считаются критическими, а сами эти объекты не должны перемещаться во время сборки мусора, если они в этот момент используются.
Соответственно, G1 GC отключает сборку мусора до тех пор, пока все потоки не покинут критические области. Это может привести к значительному увеличению времени отклика (вплоть до нескольких минут), ошибкам OutOfMemory и аварийному завершению работы JVM.
Поскольку G1 GC уже умеет блокировать определенные области на месте их расположения в памяти и не трогать их во время основной сборки, JEP 423 расширяет данную функциональность, позволяя G1 GC блокировать области и во время малой сборки. Таким образом, заблокированные области с молодыми объектами будут переходить в старшее поколение, а заблокированные области со старыми объектами не будут очищены. Как следствие, сборку мусора можно будет выполнять в обычном режиме даже при наличии критических областей.
JEP 447: Выражения перед super(…) (Preview)
Чтобы механизм наследования в Java работал корректно, инициализация конструктора должна происходить сверху вниз, т.е. конструктор класса-родителя должен сначала инициализировать поля, объявленные в этом классе, и только после этого происходит вызов конструктора класса-потомка. Для этого в Java необходимо явно вызвать конструктор класса-родителя в первом выражении, как показано в сниппете внизу:
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
super(value);
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
}
}
Но иногда это приводит к увеличению объема и излишней сложности кода. Для примера возьмем сниппет выше. Если мы хотим проверить поля конструктора до вызова конструктора класса-родителя, нам придется добавить дополнительный метод:
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
super(verifyPositive(value));
}
private static long verifyPositive(long value) {
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
return value;
}
}
JEP 447 позволяет разработчикам размещать выражения, которые не относятся к создаваемому объекту, перед явным вызовом объекта. Благодаря этому сниппет выше можно переписать таким образом:
public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
if (value <= 0)
throw new IllegalArgumentException("non-positive value");
super(value);
}
}
Это улучшение не нарушает существующие механизмы инициализации и не требует внесения изменений в JVM, но при этом позволит писать более лаконичный и поддерживаемый код.
JEP 454: Foreign Function & Memory API
JEP 454 — это финализированная фича Foreign Function & Memory API, которая позволяет Java-программам взаимодействовать с кодом вне JVM без рисков, присущих JNI.
Данный JEP включает в себя следующие улучшения:
- новая опция линкера, позволяющая клиентам передавать сегменты кучи в downcall method handles;
- новый атрибут манифеста
Enable-Native-Access
для JAR-файлов, позволяющий коду в исполняемых JAR-файлах вызывать методы с ограниченным доступом без флага командной строки--enable-native-access
; - Добавлена возможность управления вызовом нативных библиотек и дескрипторами нативных функций из Java-кода, что решает часть проблем кроссплатформенности;
- улучшенная поддержка массивов переменной длины в нативной памяти;
- поддержка произвольных кодировок для нативных строк.
JEP 456: Безымянные паттерны и переменные
JEP 456 без изменений финализирует фичу, включенную в JDK LTS 21 и позволяющую использовать безымянные паттерны и переменные. Благодаря этой фиче разработчики могут заменить знаком нижнего подчеркивания _ следующие элементы:
- неиспользуемые тип и имя компонента record при сопоставлении паттернов,
- переменные, которые необходимо объявить, но которые при этом не используются.
Данная фича значительно улучшает читаемость и поддерживаемость кода на Java.
JEP 457: Class-File API (Preview)
Файлы классов относятся к фундаментальным элементам Java-платформы. В Java есть несколько библиотек для парсинга, генерации и преобразования файлов классов, и фреймворки тоже обычно включают в себя такую библиотеку.
Но формат файлов классов постоянно совершенствуется и меняется, поэтому разработчики фреймворков не успевают вносить изменения в свои библиотеки, что может приводить к ошибкам и непредсказуемому поведению.
Даже сама Java-платформа не успевает адаптироваться под изменения в формате, поскольку в ней используются сторонние библиотеки для обработки, которые не так часто обновляются.
JEP 457 внедряет в Java стандартный API для обработки файлов классов, который будет изменяться одновременно с форматом файлов классов. Таким образом, компоненты Java не будут больше зависеть от сторонних библиотек, а разработчики фреймворков смогут реализовать автоматическую обработку файлов классов из новых версий JDK.
JEP 458: Запуск приложений, состоящих из множества файлов исходного кода
JEP 458 — это улучшение инструмента Java application launcher, которое позволяет разработчикам запускать Java-приложения, представляющие собой набор из множества файлов исходного кода. Это упростит работу на ранних этапах разработки, так как разработчикам не придется описывать конфигурацию проекта для инструмента сборки, чтобы скомпилировать несколько .java файлов. Иными словами, они могут отложить этап настройки проекта до тех пор, пока у них не будет четкого понимания структуры проекта. Таким образом, привычный цикл «настройка — сборка — запуск» сократится до «настройка — запуск».
JEP 459: Строковые шаблоны (второй Preview)
JEP 459 — это второй Preview строковых шаблонов, интегрированных в JDK 21. Данная фича обеспечивает безопасное внедрение динамически вычисляемых выражений, состоящих из фиксированного набора букв, в Java-строки, что
- упрощает работу со строками, содержащими значения, которые вычисляются во время выполнения программы, и
- устраняет риски, связанные с интерполяцией строк.
Цель второго Preview — получение обратной связи от разработчиков. JEP включает в себя незначительные изменения, связанные с типами шаблонных выражений.
JEP 460: Vector API (седьмой Incubator)
JEP 460 — это седьмой Incubator фичи Vector API, повышающей производительность векторных расчетов, которые компилируются в векторные инструкции во время выполнения приложения. Более подробно о Vector API можно прочитать в статье о фичах Java 17.
Седьмой Incubator включает в себя несколько исправлений и улучшений. В частности, для MemorySegments, лежащих в куче, теперь в качестве аргументов можно использовать любые примитивные типы, а не только массив байтов.
JEP 461: Stream Gatherers (Preview)
Stream API впервые появился в Java 8. С его помощью можно последовательно или параллельно обрабатывать потоки (последовательности значений) с применением различных промежуточных или конечных операций, таких как фильтрация, маппинг и т.д. Хотя существующие промежуточные операции достаточно эффективны, их количество ограничено, а добавление к Stream API новых необоснованно усложнит его.
JEP 461 — это новая фича, дополняющая Stream API кастомными промежуточными операциями, чтобы разработчики могли обрабатывать потоки данных в соответствии со своими нуждами. Это станет возможным благодаря новой промежуточной операции Stream::gather(Gatherer)
, которая применяет специальные gatherers к элементам потока. Gatherers представляют собой объекты интерфейса java.util.stream.Gatherer
. Они преобразуют элементы потока в соответствии с одной из четырех своих функций:
- initializer позволяет сохранять приватность объекта во время обработки элементов потока;
- integrator интегрирует новый элемент из входного потока;
- combiner может проанализировать gatherer последовательно или параллельно в случае параллельных входных потоков;
- finisher вызывается после того, как закончатся все входные элементы.
Новая фича позволит разработчикам создавать более гибкие и выразительные пайплайны потоков и писать более лаконичный и читаемый код.
JEP 462: Структурная многопоточность (второй Preview)
API структурной многопоточности улучшает управление многопоточным кодом и его мониторинг. Эта фича особенно полезна для разработчиков, работающих с виртуальными потоками, чье количество может достигать десятков тысяч. API группирует по смыслу подзадачи, выполняющиеся в разных потоках, в одну более крупную задачу. Каждая подзадача ответвляется для выполнения, а затем все они снова объединяются в одном блоке кода родительской задачи. Таким образом, задача может координировать выполнение подзадач и отслеживать ошибки их выполнения, что устраняет обычные риски, связанные с отменой и внезапным завершением работы, и повышает надежность многопоточных приложений.
JEP 462 — это второй Preview данного API, который будет включен в JDK 22 без изменений для получения обратной связи от разработчиков.
JEP 463: Неявно объявленные классы и main методы экземпляра класса (второй Preview)
JEP 463 — это второй Preview фичи, включенной в JDK 21 (JEP 445: Unnamed Classes and Instance Main Methods). Она позволяет разработчикам, только приступившим к изучению Java, писать простые программы, а затем усложнять их по мере углубления знаний. Например, простая программа Hello World без сложных, но ненужных новичкам фич будет выглядеть так:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
Второй Preview включает в себя, помимо смены названия, несколько важных улучшений функциональности:
- Упрощенное объявление классов — исходный файл без охватывающего объявления классов неявно объявляет класс с именем, выбранным хостовой системой;
- Упрощенный выбор подлежащего вызову метода main: при наличии метода main с параметром
String[]
, вызывается он. В противном случае вызывается метод main без параметров.
JEP 464: Scoped Values (второй Preview)
Scoped values — это еще одна фича, направленная на улучшение многопоточного программирования на Java. Она позволяет разработчикам расшаривать неизменяемые данные в рамках одного потока и между потоками. Ее следует использовать вместо локальных переменных потока (thread locals), поскольку она уменьшает сложность кода и повышает надежность многопоточных приложений.
JEP 464 — это второй Preview данной фичи, который будет включен в JDK 22 без изменений для получения обратной связи от разработчиков.
Переходите на Axiom JDK Pro с поддержкой любой версии Java
Axiom JDK разрабатывает и поддерживает доверенную среду разработки и исполнения Java-приложений Axiom JDK Pro. На какую бы версию вы ни хотели перейти, мы предоставляем проверенные билды с регулярными обновлениями безопасности и поддержкой от инженеров с 25-летним опытом работы с Java. Свяжитесь с нами, и мы с радостью ответим на все ваши вопросы, предоставим демо-версию и поможем с миграцией.
Подписывайтесь на наш Telegram-канал, чтобы быть в курсе новостей из мира Java.