Jdk 16 что нового
Перейти к содержимому

Jdk 16 что нового

  • автор:

Вышла Java 16

Вышла 16-я версия платформы Java SE. В этот релиз попало около двух с половиной тысяч закрытых задач и 17 JEP’ов. Изменения API можно посмотреть здесь. Release notes здесь.

Уже сейчас доступны для скачивания дистрибутивы Oracle JDK и OpenJDK.

JEP’ы, которые попали в Java 16, мы разобьём на четыре категории: язык, API, JVM и инфраструктура.

Язык

Паттерн-матчинг для оператора instanceof (JEP 375)

Оператор instanceof с паттерн-матчингом, который появился в Java 14 и перешёл во второе preview в Java 15, теперь стал стабильной синтаксической конструкцией и больше не требует флага —enable-preview . Паттерн-матчинг мы подробно рассматривали в этой статье, и с того момента в него было внесено два изменения:

Во-первых, переменные паттернов теперь не являются неявно финальными:

if (obj instanceof String s) < s = "Hello"; // OK в Java 16, ошибка в Java 15 >

Во-вторых, если тип выражения, известный на этапе компиляции, является подтипом проверяемого типа, то теперь это ошибка компиляции:

String str = . if (str instanceof String s) < // Oшибка в Java 16, OK в Java 15 >
Записи (JEP 395)

Ещё одна синтаксическая конструкция, которая стала стабильной – это записи. Она также была в режиме preview в Java 14 и Java 15. Записи мы также подробно рассматривали ранее. В Java 16 было внесено следующее изменение: теперь во внутренних классах разрешено объявлять статические члены:

public class Outer < public class Inner < // OK в Java 16, ошибка в Java 15 static void main(String[] args) < >// OK в Java 16, ошибка в Java 15 record Point(int x, int y) < >> >
sealed классы (второе preview) (JEP 397)

«Запечатанные» классы, которые появились в Java 15 в режиме preview, остаются в этом статусе. Их мы рассматривали в этой статье. Изменения по сравнению с прошлой версией следующие:

  • Теперь в спецификации языка Java появилось понятие contextual keyword взамен старым понятиям restricted keyword и restricted identifier, и одними из таких contextual keywords стали sealed , non-sealed и permits .
  • Компилятор теперь производит более строгие проверки при конверсии типов, в иерархиях которых есть sealed классы:

sealed interface Sealed < >final class Impl implements Sealed < void f(Runnable r) < Sealed s = (Sealed) r; // error: incompatible types >>

JVM

Строгая инкапсуляция внутренностей JDK по умолчанию (JEP 396)

Инкапсуляция внутренних API JDK, которая была введена в Java 9, теперь стала строгой: если в Java 9-15 значение опции —illegal-access было по умолчанию permit , то с Java 16 она становится deny . Это значит, что рефлективный доступ к защищённым членам классов и статический доступ к неэкспортированным API ( sun.* , com.sun.* , jdk.internal.* и т.д.) теперь будет выбрасывать ошибку.

Если код требует доступа к внутренностям JDK во время выполнения, то чтобы он продолжал работать на Java 16, теперь придётся явно указывать одну из трёх опций JVM:

  • —illegal-access=permit/warn/debug : открытие всех пакетов JDK
  • —add-opens=module/package=target-module : открытие одного пакета
  • —add-exports=module/package=target-module : экспортирование одного пакета (только для статического доступа)

В будущем опция —illegal-access может быть удалена окончательно. Начиная с Java 16, при её использовании выдаётся предупреждение: Option —illegal-access is deprecated and will be removed in a future release .

Изменения не касаются критического API в модуле jdk.unsupported : классы в пакетах sun.misc и sun.reflect остаются доступными без флагов.

Warnings for Value-Based Classes (JEP 390)

Классы-обёртки примитивных типов ( Integer , Double , Character и т.д.) теперь относятся к категории value-based классов, и их конструкторы, которые ранее стали deprecated в Java 9, теперь помечены как deprecated for removal.

Понятие value-based классов появилось в спецификации API Java 8. Такие классы являются неизменяемыми, создаются только через фабрики, и в их использовании не должны использоваться операции, чувствительные к identity: сравнение на == , синхронизация, identityHashCode() и т.д. Value-based классы являются кандидатами для миграции на примитивные классы в рамках проекта Valhalla, который сейчас находится в стадии активной разработки.

При синхронизации на объектах value-based классов теперь будет выдаваться предупреждение во время компиляции:

Double d = 0.0; synchronized (d) < // warning: [synchronization] attempt to synchronize on an instance of a value-based class >

Также можно включить проверки синхронизации на value-based объектах во время выполнения с помощью флагов JVM:

  • -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=1 : при попытке синхронизации будет фатальная ошибка.
  • -XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2 : при попытке синхронизации будет предупреждение.
ZGC: Concurrent Thread-Stack Processing (JEP 376)

Обработка стеков потоков в сборщике мусора ZGC теперь перенесена из safepoints в конкурентную фазу. Это позволило ещё сильнее уменьшить паузы сборщика мусора.

Unix-Domain Socket Channels (JEP 380)

Добавлена поддержка сокетов доменов Unix в socket channel и server-socket channel API. Такие сокеты используются для межпроцессного взаимодействия внутри одного хоста, и в них не используются сетевые соединения, что делает такое взаимодействие более безопасным и эффективным. Сокеты доменов Unix с недавних пор поддерживаются в Windows 10 и Windows Server 2019.

Elastic Metaspace (JEP 387)

Metaspace (пространство JVM, в котором хранятся метаданные классов) переработан для более эффективной отдачи неиспользуемой памяти обратно операционной системе и меньшего потребления памяти вне кучи в целом. Такое улучшение может быть полезно для приложений, которые интенсивно загружают и выгружают классы посредством большого количества загрузчиков классов.

Alpine Linux Port (JEP 386)

JDK теперь портирован на Alpine Linux и другие дистрибутивы Linux, которые используют musl в качестве реализации стандартной библиотеки C. Alpine Linux популярен в облаках, микросервисах и контейнерах благодаря своему маленькому размеру образа. Новый порт позволит нативно запускать JDK в этих окружениях.

Windows/AArch64 Port (JEP 388)

JDK также портирован на архитектуру Windows/AArch64. Это позволит запускать Java на компьютерах с Windows on ARM, которые в последнее время набирают популярность.

API

Новые методы в Stream

Хотя для этих двух новых методов в интерфейсе java.util.stream.Stream нет отдельного JEP, хочется упомянуть их здесь, так как это довольно заметное изменение.

Первый метод – это Stream.toList() . Этот метод собирает содержимое Stream в неизменяемый список и возвращает его. При этом, в отличие от Collectors.toUnmodifiableList() , список, который возвращается из Stream.toList() , толерантен к null -элементам.

Второй метод – это Stream.mapMulti() (и примитивные специализации). Это метод является императивным аналогом метода Stream.flatMap() : если flatMap() принимает функцию, которая для каждого элемента должна вернуть Stream , то mapMulti() принимает процедуру с двумя параметрами, где первый параметр – это текущий элемент, а второй – Consumer, в который кладутся значения. Пример:

IntStream.rangeClosed(1, 10).mapMulti((i, consumer) -> < for (int j = 1; j >); // Возвращает 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5, . 
Инструмент упаковки (JEP 392)

Инструмент создания самодостаточных приложений jpackage , который появился в Java 14 в инкубационном статусе, теперь стал постоянным модулем.

Vector API (Incubator) (JEP 338)

Появился новый инструментарий для преобразования векторных вычислений в SIMD-инструкции процессора (x64 и AArch64). Векторное API позволит разработчику контролировать процесс компиляции и не полагаться на автовекторизацию, которая в JVM является ограниченным и хрупким механизмом. Явная векторизация может применяться в таких областях как машинное обучение, линейная алгебра, криптография и др.

API находится в инкубационном модуле jdk.incubator.vector .

Foreign Linker API (Incubator) (JEP 389)

Ещё одно новое API, которое появилось в результате работы над проектом Panama – это Foreign Linker API. Это инструментарий для статического доступа к нативному коду из Java, созданный для замены JNI: он должен быть более простым в использовании, более безопасным и желательно более быстрым.

Про Foreign API делал доклад Владимир Иванов из Oracle.

Foreign-Memory Access API (Third Incubator) (JEP 393)

API для доступа вне кучи Java, которое появилось в Java 14, остаётся в инкубационном статусе с некоторыми изменениями.

Инфраструктура

Enable C++14 Language Features (JEP 347)

Кодовая база JDK до Java 16 использовала стандарты C++98/03. При этом с Java 11 код стал собираться версией с более новым стандартом, однако в нём всё ещё нельзя было использовать возможности стандарта C++11/14. Теперь же часть из этих возможностей использовать можно: в гиде по стилю HotSpot определён список возможностей C++11/14, которые можно использовать и которые нельзя.

Migrate from Mercurial to Git (JEP 357) и Migrate to GitHub (JEP 369)

Совершён переход репозиториев JDK на Git и GitHub. Миграция была полностью завершена в сентябре 2020 года, и разработка Java 16 уже полностью велась в новом репозитории.

Переход на GitHub облегчил процесс принятия изменений контрибьюторами. Теперь изменения предлагаются через привычные большинству пользователей пулл-реквесты, и большая часть процесса автоматизирована с помощью команд и ботов. Подробнее про процесс можно прочитать на странице проекта Skara.

Также сейчас обсуждается переход на Git более старых версий JDK: jdk11u и, возможно, jdk8u.

Java 16 является STS-релизом, у которого выйдет только два обновления.

Если вы не хотите пропускать новости о Java, то подписывайтесь на Telegram-канал miniJUG

Java 16 — новые синтаксические возможности языка

В марте этого года Oracle выпускает 16-ю версию Java, а уже осенью выйдет 17-я версия — следующая версия с долгосрочной поддержкой (LTS). Вряд ли за пол года появятся какие-то существенные нововведения, а потому уже сейчас можно взглянуть на то, с чем мы будем работать в ближайшие несколько лет. С момента выхода 11-й версии — текущей LTS версии Java, компанией Oracle было внедрено большое количество новых функций — от новых синтаксических конструкций до новых алгоритмов сборки мусора. В данной статье рассмотрим новые синтаксические возможности языка, появившиеся в версиях 12 — 16.

Записи (Records). JEP 395

Традиционные классы в Java довольно перегружены деталями, особенно если речь идет о POJO классах, являющихся простыми неизменяемыми (immutable) агрегатами данных. Такой класс, оформленный по правилам, содержит большое количество не очень ценного и повторяющегося кода, такого как конструкторы, методы чтения полей, методы equals(), hashCode() и toString(). Например, взгляните на класс Point, предназначенный для хранения координат на плоскости:

class Point < private final int x; private final int y; Point(int x, int y) < this.x = x; this.y = y; >int x() < return x; >int y() < return y; >public boolean equals(Object o) < if (!(o instanceof Point)) return false; Point other = (Point) o; return other.x == x && other.y == y; >public int hashCode() < return Objects.hash(x, y); >public String toString() < return String.format("Point[x=%d, y=%d]", x, y); >>

Для того, чтобы создавать такие классы было проще и компактнее, был введен новый тип класса — записи. Объявление такого класса состоит из описания его состояния, а JVM затем сама генерирует API, соответсвующее его объявлению. Это значит, что записи жертвуют некоторой свободой декларирования — возможностью отделить API класса от его внутреннего представления, но являются более компактными.

Объявление записи состоит из имени, опциональных параметров типа, заголовка и тела класса. Заголовок состоит из компонентов класса, которые являются переменными, формирующими его состояние, например:

record Point(int x, int y)

Для записей многие стандартные вещи генерируются автоматически:

  • Для каждого компонента из заголовка генерируется финальное приватное поле и метод чтения. Обратите внимание, что методы чтения именуются не стандартным для Java способом. Например, для атрибута x из класса Point метод чтения называется x(), а не getX().
  • Публичный конструктор с сигнатурой, совпадающей с заголовком класса, который инициализирует каждое поле значением, переданным при создании объекта (канонический конструктор).
  • Методы equals() и hashCode(), которые гарантируют, что 2 записи «равны», если они одного типа и имеют одинаковые значения соответствующих полей.
  • Метод toString().

Канонический конструктор можно определить явно, при этом список параметров конструктора должен быть идентичным заголовку записи, например:

record Point(int x, int y) < Point(int x, int y) < if (x < 0 || x >100 || y < 0 || y >100) < throw new IllegalArgumentException("Point coordinates must be between 0 and 100"); >this.x = x; this.y = y; > >

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

record Point(int x, int y) < Point < if (x < 0 || x >100 || y < 0 || y >100) < throw new IllegalArgumentException("Point coordinates must be between 0 and 100"); >> >

На записи накладываются некоторые ограничения:

  • Записи не могут наследоваться от других классов. Родительским классом для записи всегда является java.lang.Record. Это связано с тем, что иначе они имели бы унаследованное состояние, помимо состояния описанного в заголовке.
  • Классы записей являются финальными и не могут быть абстрактными.
  • Поля записей являются финальными.
  • Нельзя добавлять поля и блоки инициализации экземпляра.
  • Разрешается переопределять генерируемые методы, но тип возвращаемого значения должен в точности совпадать с типом значения генерируемого метода.
  • Нельзя добавлять нативные методы.

В остальном записи являются обычными классами:

  • Записи могут быть верхнеуровневыми или вложенными, могут быть параметризованными.
  • Записи могут иметь статические методы, поля и инициализаторы, а также методы экземпляра.
  • Записи могут реализовывать интерфейсы.
  • Записи могут иметь вложенные типы, в том числе и вложенные записи. Вложенные записи являются статическими по умолчанию, иначе они имели бы доступ к состоянию родительского объекта.
  • Класс записи и компоненты его заголовка могут быть декорированы аннотациями. Аннотации компонентов затем переносятся на поля, методы и параметры конструктора в зависимости от типа аннотации. Аннотации типов на типах компонентов также переносятся в места использования этих типов.
  • Объекты записей можно сериализовать и десериализовать, однако процесс сериaлизации/десериализации нельзя настраивать writeObject(), readObject(), readObjectNoData(), writeExternal(), readExternal().

Статические члены внутренних классов

Как известно внутренние классы в Java не могут иметь статических членов. Это значило бы, что внутренний класс не мог бы иметь записей. Это ограничение было ослаблено, проверил на следующем примере:

public class Outer < class Inner < private String id; private static String idPrefix = "Inner_"; Inner(String id) < this.id = idPrefix + id; >static class StaticClass < >record Point(int x, int y) < >> public static void main(String[] args) < Inner inner = new Outer().new Inner("1"); System.out.println(inner.id); Inner.StaticClass staticClass = new Inner.StaticClass(); System.out.println(staticClass); Inner.Point point = new Inner.Point(1, 2); System.out.println(point); >>
java --enable-preview --source 16 Outer.java Inner_1 jdk16.Outer$Inner$StaticClass@6b67034 Point[x=1, y=2]

Текстовые блоки. JEP 378

Традиционно, задавать в Java многострочный текст было не очень удобно:

String html + " \n" + " 

Hello, world

\n" + " \n" + "\n";

Теперь это можно сделать так:

String html = """  

Hello, world

""";

Намного лаконичнее. Есть возможность разбивать длинные строки на несколько строк для удобства восприятия. Для этого используется escape-последовательность \ , например, такую строку:

String literal = "Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit, sed do eiusmod tempor incididunt ut labore " + "et dolore magna aliqua.";

можно представить в виде:

String text = """ Lorem ipsum dolor sit amet, consectetur adipiscing \ elit, sed do eiusmod tempor incididunt ut labore \ et dolore magna aliqua.\ """;

Также появилась новая escape-последовательность \s, которая транслируется в единичный пробел (\u0020). Поскольку escape-последовательности транслируются после удаления пробелов в начале и конце строки, её можно использовать как барьер, чтобы помешать удалению пробелов. Например, в примере ниже последовательность \s используется, чтобы сделать каждую строку длиной ровно 6 символов:

String colors = """ red \s green\s blue \s """;

Паттерны для instanceof (Pattern Matching for instanceof). JEP 394

Практически в каждой программе встречается код вида:

if (obj instanceof String)

Проблема этого кода в том, что он излишне многословен. Понятно, что после проверки типа, мы захотим привести объект к нему. Почему бы не сделать это автоматически? Для упрощения этой процедуры и были введены паттерны в оператор instanceof:

if (obj instanceof String s)

Область видимости переменной s может быть как внутри блока if (как в примере выше), так и за его пределами, например:

if (!(obj instanceof String s)) < throw new Exception(); >System.out.println(s);

Переменную паттерна можно использовать и в выражении оператора if:

if (obj instanceof String s && s.length() > 5)

Однако такой пример приведет к ошибке компиляции:

if (obj instanceof String s || s.length() > 5) < // Error! . >

Переменные из паттерна могут затенять поля класса, следует быть внимательным при именовании и использовании переменных:

class Example1 < String s; void test1(Object o) < if (o instanceof String s) < System.out.println(s); // Field s is shadowed s = s + "\n"; // Assignment to pattern variable . >System.out.println(s); // Refers to field s . > > class Example2 < Point p; void test2(Object o) < if (o instanceof Point p) < // p refers to the pattern variable . >else < // p refers to the field . >> >

Изолированные типы (Sealed Classes). JEP 397

Изолированные классы и интерфейсы могут быть расширены и реализованы только теми классами и интерфейсами, которым это разрешено. Это позволяет передать компилятору знания о том, что существует ограниченная иерархия каких-либо классов. Для объявления изолированных типов используется модификатор sealed. Затем, после ключевых слов extends и implements идет ключевое слово permits, после которого перечисляются классы, которым разрешено расширять или реализовывать данный класс/интерфейс. Взглянем на пример:

package com.example.geometry; public abstract sealed class Shape permits Circle, Rectangle, Square < . >. class Circle extends Shape < . >. class Rectangle extends Shape < . >. class Square extends Shape

Классы, перечисленные после ключевого слова permits должны находиться рядом с родительским классом: в том же модуле или пакете. Если они малы и их не так много, их можно разместить в одном файле с родительским классом, в этом случае ключевое слово permits можно опустить. Каждый дочерний класс должен быть прямым наследником изолированного класса. Каждый дочерний класс должен использовать один из трех модификаторов:

  • Модификатор final, если иерархия типов не должна расширяться далее.
  • Модификатор sealed, если иерархия типов может расширяться далее, но в ограниченном ключе.
  • Модификатор non-sealed, если эта часть иерархии может расширяться произвольным образом.

Поскольку компилятор теперь обладает знанием того, что иерархия классов ограничена, это должно позволять нам перебирать типы объекта изолированного класса следующим образом:

Shape rotate(Shape shape, double angle) < if (shape instanceof Circle) return shape; else if (shape instanceof Rectangle) return shape.rotate(angle); else if (shape instanceof Square) return shape.rotate(angle); // no else needed! >

Однако, мне так и не удалось заставить такой код работать (возможно, потому что это все еще превью реализация):

public class Main < static abstract sealed class Shape permits Rect, Circle < >static final class Rect extends Shape < >static final class Circle extends Shape < >public Shape getShape(Shape shape) < if (shape instanceof Rect) return shape; else if (shape instanceof Circle) return shape; >public static void main(String[] args) < new Main().getShape(new Rect()); >>
javac -Xlint:preview --enable-preview --release 16 Main.java Main.java:9: warning: [preview] sealed classes are a preview feature and may be removed in a future release. static abstract sealed class Shape permits Rect, Circle < ^ Main.java:9: warning: [preview] sealed classes are a preview feature and may be removed in a future release. static abstract sealed class Shape permits Rect, Circle < ^ Main.java:9: warning: [preview] sealed classes are a preview feature and may be removed in a future release. static abstract sealed class Shape permits Rect, Circle < ^ Main.java:21: error: missing return statement >^ 1 error 3 warnings 

UPD: как выяснилось в комментариях, ожидание анализа на exhaustiveness в цепочках if-else появилось в результате ошибочного понимания объяснения в JEP.

Switch выражения (Switch Expressions). JEP 361

Использование оператора switch чревато ошибками из-за его сквозной семантики. Взгляните на пример:

switch (day)

Из-за большого количества ключевых слов break легко запутаться и пропустить его где-то.

Кроме того, очень часто оператор switch используется для эмуляции switch выражения, но это не удобно и тоже чревато ошибками:

int numLetters; switch (day)

Для решения перечисленных проблем был введен новый способ записи условий в операторе switch в виде «case L ->» и сам оператор стал еще и выражением.

Если условие записано в виде «case L ->», то при его срабатывании выполняется только инструкция справа от него. Сквозная семантика в этом случае не работает. Пример такой записи:

static void howMany(int k) < switch (k) < case 1 ->System.out.println("one"); case 2 -> System.out.println("two"); default -> System.out.println("many"); > >

Теперь рассмотрим пример switch выражения:

static void howMany(int k) < System.out.println( switch (k) < case 1 ->"one"; case 2 -> "two"; default -> "many"; > ); >

Большинство выражений будут иметь единственную инструкцию справа от условия «case L ->». На случай, если понадобится целый блок, вводится ключевое слово yield для возврата значения из выражения:

int j = switch (day) < case MONDAY ->0; case TUESDAY -> 1; default -> < int k = day.toString().length(); int result = f(k); yield result; >>;

Условия в switch выражении должны быть исчерпывающими, то есть охватывать все возможные варианты. На практике это означает, что обязательно присутствие общего условия — default (в случае с простым оператором switch это не обязательно). Однако, в случае со switch выражениями на enum типах, которые покрывают все возможные константы, наличие общего условия необязательно. В таком случае, при добавлении новой константы в enum, компилятор выдаст ошибку, чего не случилось бы, будь общее условие задано.

Заключение

В данной статье мы рассмотрели новые синтаксические возможности Java 16: записи, текстовые блоки, паттерны для instanceof, изолированные типы и switch выражения. Стоит отметить, что изолированные типы все еще находятся на стадии preview, а потому в Java 17 могут и не войти.

Ссылки

  • JEP 395. Records
  • JEP 378. Text Blocks
  • JEP 394. Pattern Matching for instanceof
  • JEP 397. Sealed Classes (Second Preview)
  • JEP 361. Switch Expressions

JDK 16: Что нового принесет Java 16

В Java Development Kit (JDK) 16 в конце октября были добавлены еще две предлагаемые новые функции, API доступа к внешней памяти и сопоставления с образцом (прим.пер. Pattern Matching). Ранее предложенные функции включают в себя готовый к производству инструмент для упаковки приложений, thread-stack процессинг при сборке мусора, поддержку функций языка C++ 14 и возможность “эластичного metaspace” для более быстрого возврата неиспользуемой памяти для метаданных классов в ОС.

JDK 16 будет эталонной реализацией версии стандартного набора Java, следующей за JDK 15, вышедшим 15 сентября. График выпуска JDK 16 предполагает стадии рампдаунов(пре-релизов) 10 декабря и 14 января 2021 года, а затем 4 февраля и 18 февраля 2021 года выпускаются релиз-кандидаты. Промышленный релиз намечен на 16 марта 2021 года.

По состоянию на 4 ноября 2020 года в JDK 16 официально предполагается внедрить тринадцать предложений. Новые возможности Java 16 включают:

  • Перемещение thread-stack процессинга ZGC (Z Garbage Collector) из безопасных точек (safepoints) в параллельную фазу. Планируется: удаление thread-stack процессинга из безопасных точек ZGC; теперь thread-stack процессинг станет ленивым, кооперативным, параллельным и инкрементальным; удаление всех других корневых процессов для каждого потока из безопасных точек ZGC; и предоставление механизма для других подсистем виртуальной машины HotSpot для ленивой обработки стеков. ZGCпредназначен для того, чтобы сделать паузы GC и проблемы масштабируемости в HotSpot делом прошлого. До сих пор операции GC, время исполнения которых кореллировало с размером кучи и размером Metaspace, были перемещены из безопасных точек в параллельные фазы. Это такие операции как маркировка, перемещение, обработка ссылок, выгрузка классов и большая часть обработки корневых ссылок. Единственные действия, которые все еще выполняются в безопасных точках GC, — это обработка корневых подмножеств и ограниченная по времени операция завершения маркировки. Эти корневые ссылки размещались в стеках потоков Java и в других корневых ссылках, причем эти корневые ссылки были проблематичными, поскольку они дублировались в каждом потоке. Чтобы выйти из текущей ситуации, обработка каждого потока, включая сканирование стека, должна быть переведена в параллельную фазу. При таком подходе стоимость задержки должна быть незначительной, а время, затрачиваемое на безопасных точках ZGC на типичных машинах, должно составлять менее одной миллисекунды.
  • Возможность эластичного Metaspace, которая быстрее возвращает в ОС неиспользуемую для метаданных память HotSpotVM, сокращает ресурсы, требуемые для Metaspace и упрощает его код, что снижает затраты на обслуживание. У Metaspace были проблемы с высоким использованием памяти вне кучи. Планируется замена существующего менеджера памяти на схему buddy-allocation, которая делит память на блоки, наиболее подходящие под запросы на выделение памяти. Этот подход был использован, среди прочего, в ядре Linux, и позволит выделять память меньшими кусками, чтобы снизить потребности загрузчика классов. Фрагментация также уменьшится. Кроме того, лениво, по мере необходимости, будут выделяться новые блоки памяти за счёт ОС, что позволит уменьшить объем памяти для загрузчиков, требующих при старте большого объёма памяти, но не использующие весь объём сразу или не нуждающиеся в их полном использовании вообще. Чтобы полностью использовать эластичность, предлагаемую buddy-allocation, память Metaspace будет организована в одинаковые по размеру блоки, которые могут выделяться или нет, независимо друг от друга.
  • Включение возможностей языка C++ 14, JDK позволит использовать возможности C++ 14 в исходном коде с конкретными указаниями о том, какие из этих возможностей могут быть использованы для виртуальной машины HotSpot. В соответствии с JDK 15 языковые возможности, используемые кодом C++ в JDK, были ограничены языковыми стандартами C++ 98/03. В JDK 11 исходный код был обновлен для поддержки более поздних стандартов C++. В том числе появилась возможность сборки с последними версиями компиляторов, поддерживающих функции языка C++ 11/14. Это предложение не предлагает изменений стиля или использования кода C++, за пределами HotSpot. Но чтобы воспользоваться преимуществами возможностей языка C++, требуются некоторые изменения во время сборки, в зависимости от компилятора платформы.
  • Векторный API на стадии инкубатора, в котором JDK будет оснащен модулем инкубатора, jdk.incubator.vector для выражения векторных вычислений, которые компилируются в оптимальные аппаратные векторные инструкции на поддерживаемых архитектурах ЦП, и позволят достичь более высокой производительности по сравнению с эквивалентными скалярными вычислениями. Vector API предоставляет механизм для написания сложных векторных алгоритмов на Java, используя уже существующую поддержку в виртуальной машине HotSpot для векторизации, но с пользовательской моделью, которая делает векторизацию более предсказуемой и надежной. Цели этого предложения включают в себя предоставление четкого и ясного API для выражения целого ряда векторных вычислений, будучи платформо-независимым за счёт поддержки нескольких архитектур процессоров, а также предлагая надежную компиляцию во время выполнения и производительность на архитектурах x64 и AArch64. Целью также является отказоустойчивость, когда векторное вычисление будет постепенно деградировать, но продолжать исполняться, если оно не может быть полностью выражено в runtime (во время выполнения) в виде последовательности аппаратных векторных инструкций, либо потому, что архитектура не поддерживает некоторые инструкции, либо не поддерживается архитектура процессора.
  • Перенос JDK на платформу Windows/AArch64. Выпуск нового класса серверов и потребительского оборудования вызвал спрос, сделавший AArch64 (ARM64) Windows/AArch64 важной платформой. Хотя само портирование уже в основном завершено, основное внимание в этом предложении уделяется интеграции порта в магистральный репозиторий JDK.
  • Перенос JDK на AlpineLinux и другие дистрибутивы Linux, использующие musl в качестве основной библиотеки C, на архитектуры x64 и AArch64. Musl-это Linux-реализация функциональности стандартной библиотеки, описанной в стандартах ISOC и Posix. AlpineLinuxшироко распрстранена в облачном развертывании, микрослужбах, и контейнер-среде благодаря небольшому размеру образа. Размер образа Docker для Linux меньше 6 МБ. Готовность Jawa к работе «из коробки» позволит в Tomcat, Jetty, Spring, и другим популярным платформам сразу нативно работать в таких условиях. Используя jlink для уменьшения размера Java runtime, пользователь может создать еще меньший образ, адаптированный для запуска конкретного приложения.
  • Предоставление классов записей (Records), которые действуют как прозрачные носители для неизменяемых данных. Записи можно считать набором данных. Записи были предварительно представлены в JDK 14 и JDK 15. Это является ответом на жалобы на излишнюю многословность Java, и на то, что она содержала слишком много «церемоний». Цели включают в себя разработку объектно-ориентированной конструкции, которая просто агрегирует значения, помогая разработчикам сосредоточиться на моделировании неизменяемых данных, а не на расходящихся вариантах поведения, автоматически реализуя такие методы, как equals и accessors, и соответствует давнему принципу Java — строгой типизации.
  • Добавление каналов сокетов Unix-domain, в которых поддержка сокетов Unix-domain (AF_UNIX) добавляется к API-интерфейсам socketchannel и serversocketchannel в пакете nio.channels. План также расширяет механизм наследования для поддержки каналов сокетов Unix-домена и каналов сокетов сервера. Unix-сокеты используются для межпроцессного взаимодействия на одном узле. Они во многих отношениях похожи на сокеты TCP/IP, за исключением того, что адресация происходит именами путей файловой системы, а не IP-адресами и номерами портов. Цель новой возможности-поддержка всех функций Unix-доменных каналов сокетов, которые являются общими для основных платформ Unix и Windows. Каналы сокетов Unix-домена будут вести себя так же, как существующие каналы TCP/IP с точки зрения чтения/записи, настройки соединения, приема входящих соединений серверами и мультиплексирования с другими неблокирующими каналами в селекторе. Сокеты Unix-домена являются более безопасными и более эффективными, чем петлевые соединения TCP/IP для локальных межпроцессных коммуникаций.
  • API доступа к внешней памяти, позволяющий Java-программам безопасно обращаться к внешней памяти за пределами кучи Java. Ранее инкубированный как в JDK 14, так и в JDK 15, API доступа к внешней памяти будет повторно инкубирован в JDK 16, с добавлением уточнений. Изменения включая более четкое разделение ролей между интерфейсами MemorySegment и MemoryAddresses . Цели этого предложения включают предоставление единого API для работы с различными видами внешней памяти, включая собственную, постоянную и управляемую память кучи. API не должны подрывать безопасность виртуальной машины Java. Это предложение мотивированно тем, что многие программы Java, такие как Ignite, Memcached и MapDB, обращаются к внешней памяти. Но Java API не предоставляет удовлетворительного решения для доступа к внешней памяти.
  • Сопоставление с образцом для оператора instanceof, который также был предварительно просмотрен как в JDK 14, так и в JDK 15. Он будет доработан в JDK 16. Сопоставление с образцом позволяет более сжато и безопасно выразить общую логику в программе, а именно условное извлечение компонентов из объектов.
  • Предоставление инструмента для упаковки автономных Java-приложений. Введенный в качестве инкубационного инструмента в JDK 14, jpackage оставался в инкубации в JDK 15. С JDK 16 jpackage переходит в производство, поддерживая собственные форматы пакетов, чтобы дать пользователям опыт нормальной установки и позволить задавать параметры времени запуска во время упаковки. Форматы включают msi и exe в Windows, pkg и dmg в MacOS, а также deb и rpm в Linux. Инструмент может быть вызван непосредственно из командной строки или программно. Новый инструмент упаковки решает ситуацию, в которой многие приложения Java должны быть установлены на собственных платформах первоклассным способом, а не помещены на путь класса или путь модуля. Необходим устанавливаемый пакет, подходящий для нативной платформы.
  • Миграция репозиториев исходного кода OpenJDK из Mercurial в Git. Причиной этих изменений являются преимущества в размере метаданных системы контроля версий, а также доступные инструменты и хостинг.
  • Миграция на GitHub, связанная с миграцией Mercurial-to-Git, с репозиториями исходного кода JDK 16, которые будут находиться на популярном сайте код-шэринга. Переход на Git, GitHub и Skara для Mercurial JDK и JDK-sandbox был произведен 5 сентября и остается открытым для участия.

Сборки раннего доступа JDK 16 для Linux, Windows и MacOS можно найти по адресу jdk.java.net. Как и JDK 15, JDK 16 будет краткосрочным релизом, поддерживаемым в течение шести месяцев. JDK 17, ожидаемый в сентябре 2021 года, будет выпуском с долгосрочной поддержкой (LTS), и будет поддерживаться в течение нескольких лет. Текущая версия LTS, JDK 11, была выпущена в сентябре 2018 года.

Java в 2021 году: обновления 16 и 17, популярность в Азии и стабильность в рейтингах

Java-университет

Java в 2021 году: обновления 16 и 17, популярность в Азии и стабильность в рейтингах - 1

На дворе — конец декабря, а значит, пора подводить итоги. Ведь в мире Java-разработки произошло много изменений в течение этого года. В этом тексте мы собрали основные.

Обновления Java: 16 и 17

Раз в полгода язык Java получает обновление. Ожидаемо, в 2021 году вышли два релиза — Java 16 и Java 17. В релиз Java 16 вошло 17 улучшений. Среди новшеств 16-й версии: новый инструмент упаковки для доставки автономных Java-приложений, улучшенное управление памятью, дополнительные функции инкубации и предварительного просмотра, усовершенствования, устраняющие несовместимый с будущим код, а также новые порты для JDK для Alpine Linux и других Linux-дистрибутивов. Java 16 завершили сопоставление шаблонов для instanceof и Record, языковых улучшений, впервые появившихся в Java 14. В Java 17, вышедшую в сентябре 2021 года, вошли 14 изменений. Большая часть изменений касается увеличения производительности языка и платформы в целом. Также создатели убрали много устаревших компонентов и технологий, ставших неактуальными из-за отсутствия поддержки со стороны ПО от сторонних разработчиков. В ключевые изменения вошла улучшенная поддержка sealed-классов и интерфейсов, блокирующих возможность наследовать свойства из этих компонентов объектами и другими классами (JEP 409). Стоит отметить и усовершенствованную блокировку доступа к внутренним API платформы JDK, а также улучшение производительности за счет использования технологии Vector API, удаление экспериментальных компиляторов AOT и JIT и использование фреймворка Metal для рендеринга на устройствах Apple. Коммерческие сборки JDK 17 от Oracle для Windows и других платформ доступны на сайте компании-разработчика. Бесплатная версия OpenJDK 17 с открытым исходным кодом и ежеквартальными обновлениями доступна для загрузки по этой ссылке. JDK 17 имеет долгосрочную (LTS) поддержку в течение 8 лет. Следующий выпуск LTS Java выйдет в 2023 году с Java 21. Это изменит периодичность выпуска LTS с трех до двух лет.

Изменения в экосистеме Java: возрождение Java EE и поддержка Microsoft

Что еще происходило с Java в 2021 году? Например, в начале апреля 2021 года закончилось многолетнее разбирательство между Oracle и Google. Верховный суд США встал на сторону Google в споре с Oracle на тему незаконного использования API языка Java. Череда судебных процессов затянулась на 11 лет, различные инстанции признавали победу то за Google, то за Oracle. За прошедшие годы сумма, которую Google могла бы выплатить в случае проигрыша, выросла с первоначальных 6,1 миллиарда до 9,3 миллиарда долларов. Среди изменений 2021 года стоит вспомнить и об участии Microsoft. В апреле компания опубликовала экспериментальные сборки собственного дистрибутива OpenJDK для Windows, Linux и macOS, который поддерживает работу ряда систем в инфраструктуре корпорации. Загрузить и установить набор средств разработки и выполнения приложений на популярном языке программирования Java может бесплатно любой желающий. В сентябре, в то же время, что и релиз Java 17, вышло исследование о том, что Java EE переживает возрождение. Согласно отчету, Jakarta EE была второй по популярности облачной платформой, которую использовали 47% опрошенных разработчиков. «Опрос разработчиков Jakarta EE в 2021 году показывает использование Jakarta EE по всем направлениям и является еще одним показателем того, что это подходящая платформа для облачных инноваций Java», — сказала Мелисса Моджески, вице-президент платформы приложений и интеграций в IBM.

Java в рейтингах: выбирают чаще, чем Python

Одно из главных заключений ежегодного исследования компании JetBrains — это то, что язык программирования Java хоть и немного теряет позиции, все же более популярен, чем Python в качестве основного языка (32% против 29% опрошенных). Согласно графику компании RedMonk, по итогам июня 2021 года язык программирования Java поднялся в рейтинге с третьего на второе место. Теперь он делит вторую позицию с Python. Первое место в рейтинге RedMonk занимает язык JavaScript. А вот в рейтинге языков программирования TIOBE по состоянию на декабрь 2021 года, Java опустилась со второго на третье место по популярности. В ежегодном опросе сообщества Stack Overflow Java, как и в прошлом году, занял пятое место. Как видим, популярность Java стабильна, хоть и просела на несколько позиций. Этот язык по-прежнему остается актуальным для разработки.

Что происходило с версиями Java?

Как и в 2020 году, Java 8 по-прежнему ведущая версия этого языка программирования. Интересно и то, что начиная с 2019 года Java 11 ежегодно демонстрирует стабильный рост примерно на 10 процентных пунктов. В цифрах это выглядит так: 72% разработчиков, которые используют Java, отдают предпочтение Java 8, а 42% — Java 11, говорится в исследовании JetBrains. Oпрос, проведенный сообществом Snyk, также показал, что разработчики начали переходить от Java 8 к Java 11, последнему выпуску LTS, предшествовавшему Java 17. Предыдущие отчеты сообщества Java показали, что разработчики в основном использовали Java 8. Согласно опросу, 61,5% респондентов использовали Java 11 в проектах, в то время как 12% использовали последнюю версию Java (на тот момент Java 15). Доли Java 12 и Java 13 снизились примерно в три раза с 2020 года, в то время как более новые Java 14 и Java 15 уже получили аудиторию несмотря на то, что они относительно новые.

Где и для чего использовали Java?

Java в 2021 году: обновления 16 и 17, популярность в Азии и стабильность в рейтингах - 2

Как и в 2020 году, Java чаще всего используют в Южной Корее, Китае и Германии. Доля Java в Южной Корее составляет 55%, в Китае — 47%, а в Германии — 33%. В Беларуси 26% разработчиков предпочитают Java, в Украине — 20%, в России — 18%. Это немного меньше, чем в 2020 году. Чаще всего Java использовали для разработки вебсайтов (39%), реже — для создания утилит (26%) и системного ПО (19%). Использование Java для веб-сайтов и ИТ-инфраструктуры незначительно выросло в период с 2020 по 2021 год, в то время как Java стала меньше использоваться для бизнес-аналитики, обработки данных и машинного обучения. Среди фреймворков Java лидирует Spring Boot (его выбирают 65% программистов). На втором месте фреймворк Spring MVC — 42% разработчиков. Так прошел 2021 год для мира Java-разработки. Что, по-вашему, стало главным событием для сообщества Java в уходящем году?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *