Выпуск платформы Java SE 22 и открытой эталонной реализации OpenJDK 22

После шести месяцев разработки компания Oracle выпустила платформу Java SE 22 (Java Platform, Standard Edition 22), в качестве эталонной реализации которой используется открытый проект OpenJDK. За исключением удаления некоторых устаревших возможностей в Java SE 22 сохранена обратная совместимость с прошлыми выпусками платформы Java — большинство ранее написанных Java-проектов без изменений будут работоспособны при запуске под управлением новой версии. Готовые для установки сборки Java SE 22 (JDK, JRE и Server JRE) подготовлены для Linux (x86_64, AArch64), Windows (x86_64) и macOS (x86_64, AArch64). Разработанная в рамках проекта OpenJDK эталонная реализация Java 22 полностью открыта под лицензией GPLv2 с исключениями GNU ClassPath, разрешающими динамическое связывание с коммерческими продуктами.

Java SE 22 отнесён к категории выпусков с обычным сроком поддержки, обновления для которого будут выпускаться до следующего релиза. В качестве ветки с длительным сроком поддержки (LTS) следует использовать Java SE 21 или Java SE 17, обновления для которых будут выпускаться до 2031 и 2029 годов соответственно (общедоступные — до 2028 и 2026 годов). Общедоступная поддержка LTS-ветки Java SE 11 прекращена в сентябре прошлого года, но расширенная поддержка будет осуществляться до 2032 года. Расширенная поддержка LTS-ветки Java SE 8 продлится до 2030 года.

Напомним, что начиная с выпуска Java 10 проект перешёл на новый процесс разработки, подразумевающий более короткий цикл формирования новых релизов. Новая функциональность теперь развивается в одной постоянно обновляемой master-ветке, в которую включаются уже готовые изменения и от которой раз в шесть месяцев ответвляются ветки для стабилизации новых выпусков.

Из новшеств Java 22 можно отметить:

  • В сборщике мусора G1 реализована поддержка закрепления областей (region pinning), позволяющая временно зафиксировать местоположение Java-объектов в памяти, чтобы избежать их перемещения сборщиком мусора и обеспечить безопасную передачу ссылок на эти объекты между Java и нативным кодом. Закрепление позволяет снизить задержки и обойтись без отключения сборки мусора при выполнении критических секций (critical regions) JNI (Java Native Interface) с нативным кодом (во время выполнения данных секций JVM не должен перемещать связанные с ними критические объекты для того чтобы избежать состояний гонки). Закрепление выводит критические объекты из поля зрения сборщика мусора, который может продолжать выполнять чистку незакреплённых областей.
  • Добавлена предварительная возможность, разрешающая указание в конструкторах выражений перед вызовом super(…), используемого для явного вызова конструктора родительского класса из конструктора наследуемого класса, если эти выражения не ссылаются на создаваемый конструктором экземпляр.
    
       class Outer {
           void hello() {
               System.out.println("Hello");
           }
           class Inner {
               Inner() {
                   hello();
                   super();
               }
           }
       }
    
  • Стабилизирован API FFM (Foreign Function & Memory), позволяющий организовать взаимодействие Java-программ с внешними кодом и данными через вызов функций из внешних библиотек и доступ к памяти вне JVM, не прибегая к использованию JNI (Java Native Interface).
  • Включена поддержка безымянных переменных и сопоставлений с шаблоном — вместо неиспользуемых, но необходимых при вызове переменных и шаблонов, теперь можно указывать символ «_».
    
       // было
       String pageName = switch (page) {
         case GitHubIssuePage(var url, var content, var links, int issueNumber)
               -› "ISSUE #" + issueNumber;
       ...
       };
    
    
       // теперь можно
       String pageName = switch (page) {
          case GitHubIssuePage(_, _, _, int issueNumber)
            -> "ISSUE #" + issueNumber;
       };
    
  • Предложена предварительная реализация API Class-File для разбора, генерации и преобразования файлов с классами Java.
    
       ClassFile cf = ClassFile.of();
       ClassModel classModel = cf.parse(bytes);
       byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
            classBuilder -› {
                for (ClassElement ce : classModel) {
                    if (!(ce instanceof MethodModel mm
                            && mm.methodName().stringValue().startsWith("debug"))) {
                        classBuilder.with(ce);
                    }
                }
            });
    
  • В утилите java реализована возможность запуска Java-программ, поставляемых в форме нескольких файлов с кодом или предкомпилированных библиотек классов, без раздельной компиляции этих файлов и без конфигурирования сборочной системы. Новая возможность упрощает запуск программ, в которых код разных классов выделен в отдельные файлы.
    
       Prog.java:
    
          class Prog {
              public static void main(String[] args) { Helper.run(); }
          }
    
       Helper.java:
    
          class Helper {
              static void run() { System.out.println("Hello!"); }
          }
    

    Например, для запуска программы, состоящей из двух файлов «Prog.java» и «Helper.java» теперь достаточно запустить «java Prog.java», что приведёт к компиляции класса Prog, определения ссылки на класс Helper, поиска и компиляции файла Helper.java и вызова метода main.

  • Добавлена вторая предварительная реализация строковых шаблонов (String Template), реализованных в дополнение к строковым литералам и блокам текста. Строковые шаблоны позволяют совмещать текст с вычисляемыми выражениями и переменными без использования оператора «+». Подстановка выражений осуществляется при помощи подстановок {..}, при этом для проверки корректности подставляемых значений могут подключаться специальные обработчики. Например, обработчик SQL обеспечивает проверку значений, подставляемых в SQL-код, и возвращает на выходе объект java.sql.Statement, а обработчик JSON отслеживает корректность подстановок JSON и возвращает JsonNode.
    
       String query = "SELECT * FROM Person p WHERE p."
           + property + " = '" + value + "'"; // было
    
       Statement query = SQL."""SELECT * FROM Person p
           WHERE p.{property} = '{value}'"""; // стало
    
  • Добавлена седьмая предварительная реализация API Vector, предоставляющего функции для векторных вычислений, которые выполняются с использованием векторных инструкций процессоров x86_64 и AArch64 и позволяют одновременно применить операции сразу к нескольким значениям (SIMD). В отличие от предоставляемых в JIT-компиляторе HotSpot возможностей по автовекторизации скалярных операций, новый API даёт возможность явно управлять векторизацией для параллельной обработки данных.
  • Добавлена предварительная реализация расширенного API Stream, поддерживающего определение собственных промежуточных операций, которые могут оказаться полезны в случаях, когда существующих встроенных промежуточных операций недостаточно для желаемого преобразования данных. Собственные обработчики подключаются при помощи новой промежуточной операции
    Stream::gather(Gatherer), которая обрабатывает элементы потока, применяя к ним заданный пользователем обработчик.

    
       jshell› Stream.of(1,2,3,4,5,6,7,8,9).gather(new WindowFixed(3)).toList()
       $1 ==› [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    
  • Предложен для тестирования второй вариант экспериментального API для cтруктурированного параллелизма (Structured Concurrency), упрощающего разработку многопоточных приложений за счёт обработки нескольких задач, выполняемых в разных потоках, как единого блока.
  • Добавлена вторая предварительная реализация неявно объявленных классов и безымянных экземпляров метода «main», в которых можно обойтись без объявлений public/static, передачи массива аргументов и прочих сущностей, связанных с объявлением класса.
    
       // было
       public class HelloWorld {
         public static void main(String[] args) {
           System.out.println("Hello world!");
         }
       }
    
       // теперь можно
       void main() {
           System.out.println("Hello, World!");
       }
    
  • Добавлена вторая предварительная реализация ограниченных значений (Scoped Values), позволяющих совместно использовать неизменяемые данные в потоках и эффективно обмениваться данными между дочерними потоками (значения наследуются). Scoped Values развиваются для замены механизма переменных локальных к потоку (thread-local variables) и более эффективны при использовании очень большого числа виртуальных потоков (тысячи и миллионы потоков). Главное отличие Scoped Values от переменных локальных к потоку в том, что первые записываются один раз, в дальнейшем не могут быть изменены и остаются доступны только на время выполнения потока.
  • В сборщике мусора Parallel повышена производительность работы с большими массивами объектов. Оптимизация позволила в некоторых тестах с большими массивами объектов на 20% снизить задержку перед началом поиска объекта.

Дополнительно можно отметить публикацию обновления платформы для создания приложений с графическим интерфейсом JavaFX 22.

Источник: http://www.opennet.ru/opennews/art.shtml?num=60812