JavaRush /Java блог /Java Developer /Из 8 в 13: полный обзор версий Java. Часть 2
Константин
36 уровень

Из 8 в 13: полный обзор версий Java. Часть 2

Статья из группы Java Developer
Эта статья является второй частью моего обзора по нововведениям в Java версий 8-13. Первая часть — здесь. Без лишних предисловий двигаемся дальше: в 25 сентября 2018, когда и вышла новая JDK:

Java 11

Из 8 в 13: полный обзор версий Java. Часть 2 - 1

var (в лямбде)

Отныне и во веки веков мы можем указать типы лямбда-параметров или пропустить их при написании лямбда-выражения (неявно типизированных лямбда-выражений):

Function<String, String> append = (var string) -> string + " Text";
String appendedString = append.apply("Some");
System.out.println(appendedString);
Также можно добавлять аннотации к лямбда-параметрам без необходимости писать полное имя типа переменной:

Function<String, String> append = (@NonNull var string) -> string + " Text";

Z (ZGC)

ZGC — это новый сборщик мусора, который не работает. Он выделяет новую память, но никогда не перезапускает ее. ZGC обещает управлять большими объемами памяти с высокой пропускной способностью и коротким временем паузы (ZGC доступен только на 64-битных платформах). Эталонная окраска — ZGC использует 64-битные указатели с техникой, называемой окрашиванием указателей. Цветные указатели хранят дополнительную информацию об объектах в куче. Когда память становится фрагментированной, это помогает избежать снижения производительности, когда GC необходимо найти место для нового распределения. Сборка мусора с помощью ZGC состоит из таких этапов:
  1. остановки мира: мы ищем отправные точки для достижения объектов в куче (например, локальных переменных или статических полей);
  2. пересечение граф объектов, начиная с корневых ссылок. Мы отмечаем каждый объект, который достигаем (ZGC ходит по графу объектов и исследует цветные указатели, отмечая доступные объекты);
  3. обработки некоторых крайних случаев, например, слабых ссылок;
  4. перемещение живых объектов, освобождая большие участки кучи, чтобы ускорить распределение.
  5. когда начинается фаза перемещения, ZGC разделяет кучу на страницы и работает по одной странице за раз;
  6. ZGC заканчивает движение любых корней, и происходит остальная часть перемещения.
Данная тема весьма сложная и запутанная. Подробное рассмотрение тянет на отдельную статью, поэтому просто оставлю это здесь:

Epsilon GC

Эпсилон — это сборщик мусора, который обрабатывает выделение памяти, но не реализует какой-либо реальный механизм ее восстановления памяти. Как только доступная куча Java будет исчерпана, JVM закроется. То есть, если в бесконечном массиве запустить создание объекта без привязки к ссылке с данным сборщиком мусора, приложение упадёт с OutOfMemoryError (а если с любым другим нет, так как он будет подчищать объекты без ссылок). Зачем он нужен? А вот зачем:
  1. Тестирование производительности.
  2. Тестирование давления памяти.
  3. Тестирование интерфейса VM.
  4. Чрезвычайно недолгая работа.
  5. Улучшения латентности последней капли.
  6. Улучшения пропускной способности последней капли.
Полезные ссылки: Другие нововведения:
  1. ByteArrayOutputStream получил метод void writeBytes(byte []), записывающий все байты из аргумента в OutputStream.
  2. FileReader и FileWriter получили новые конструкторы, позволяющие указывать Charset.
  3. Path отхватил два новых метода, of(String, String []) возвращает Path из строкового аргумента пути или последовательности строк, которые при объединении образуют строку пути и of(URI): возвращает Path из URI.
  4. Pattern — получил метод asMatchPredicate(), который проверяет, соответствует ли заданная строка ввода, заданному шаблону (позволяет ли создать предикат по регулярному выражению, чтобы можно было, например, фильтровать данные в stream).
  5. String отхватил много полезных методов, таких как:
    • String strip(): вернёт нам строку, которая является этой строкой, при этом удаляются все пробелы в начале и в конце строки (аналог trim(), но по-другому определяет пробелы);
    • String stripLeading(): вернёт нам строку, которая является этой строкой, при этом удаляются все пробелы в начале строки;
    • String stripTrailing(): вернёт нам строку, которая является этой строкой, при этом удаляются все пробелы в конце строки;
    • Stream lines(): вернёт нам Stream из String, извлеченных из этой строки, поделенных разделителями строк;
    • String repeat(int): вернёт нам строку, которая представляет собой конкатенацию этой строки, повторяющееся количество раз.
    • boolean isBlank(): вернёт нам true, если строка пуста или содержит только пробелы, иначе false.
  6. Thread — были удалены методы destroy() и stop(Throwable).
  7. Files получил ряд новых методов:
    • String readString(Path): читает все данные из файла в строку, при этом декодируя из байт в символы с использованием кодировки UTF-8;
    • String readString(Path, Charset): так же, как и в методе выше, с разницей в том, что декодирование из байт в символы происходит с использованием указанной Charset;
    • Path writeString (Path, CharSequence, OpenOption []): записывает последовательность символов в файл. Символы кодируются в байты, используя кодировку UTF-8;
    • Path writeString(Path, CharSequence,Charset, OpenOption []): такой же метод, что и выше, только символы кодируются в байты, используя кодировку, указанную в Charset.
Это были самые интересные нововведения API (по моему скромному мнению), вот пара материалов для более детального ознакомления:

Java 12

Проходит полгода, и мы видим следующую ступень эволюции Java. Значит, пора доставать лопатку знаний и копать. Из 8 в 13: полный обзор версий Java. Часть 2 - 2

Update G1

Для G1 были внесены такие улучшения:
  1. Возврат неиспользуемой выделенной памяти

    В Java heap memory есть такое понятие как неиспользуемая память (или по-другому — неактивная). В Java 12 решили пофиксить эту проблему, теперь:

    • G1 возвращает память из кучи в полном GC или во время параллельного цикла; G1 старается предотвратить полный GC и запускает параллельный цикл, исходя из распределения кучи. Придется принуждать G1 к возвращению памяти из кучи.

    Данное улучшение фокусируется на быстродействии за счет автоматического возврата памяти из кучи в ОС, когда G1 не используется.

  2. Прерывание смешанных коллекций, когда время паузы превышено

    G1 использует механизм анализа для выбора объема работы, необходимого для сбора мусора. Он собирает живые объекты без остановки после определения набора и запуска очистки. Это приводит к тому, что сборщик мусора превышает целевое значение времени паузы. Собственно, такую проблему и решает улучшение, так как если время выполнения следующего шага выходит за рамки разумного, этот шаг можно прервать.

Microbenchmark

В Java 12 ввели тесты микробенчмаркинга, чтобы производительность JVM легко тестировалась с помощью уже существующих тестов. Это было бы очень полезно для всех, кто хочет работать над самой JVM. Добавляемые тесты создаются с использованием Java Microbenchmark Harness (JMH). Эти тесты позволяют проводить непрерывное тестирование производительности на JVM. JEP 230 предлагает ввести около 100 тестов, причем новые тесты вводятся по мере выпуска новых версий Java. Вот пример добавляемых тестов.

Shenandoah

Это алгоритм сборки мусора (GC), цель которого — гарантировать низкое время отклика (нижний предел — 10-500 мс). Это уменьшает время паузы GC при выполнении работы по очищению одновременно с работающими потоками Java. В Shenandoah время паузы не зависит от размера кучи. Это означает, что время паузы будет одинаковым независимо от размера вашей кучи. Это экспериментальная функция, которая не включена в стандартную (Oracle) сборку OpenJDK.

Improve Switch

В Java 12 улучшены выражения Switch для сопоставления с образцом. Был введен, новый синтаксис L →. Вот список ключевых моментов нового switch:
  1. Новый синтаксис устраняет необходимость в операторе break для предотвращения ошибок.
  2. Выражения переключателя больше не проваливаются.
  3. Кроме того, мы можем определить несколько констант в одной метке.
  4. default регистр теперь обязателен в выражениях переключателей.
  5. break используется в выражениях Switch для возврата значений из самого регистра (по сути switch может возвращать значения).
Рассмотрим как пример:

var result = switch (someDay) {
  case "M", "W", "F" -> "MWF";
  case "T", "TH", "S" -> "TTS";
  default -> {
      if(someDay.isEmpty())
            break "Please insert a valid day.";
      else
            break "Looks like a Sunday.";
  }
};
Definitive Guide To Switch Expressions In Java 13 Другие нововведения:
  1. String:

    transform(Function f) — применяет предоставленную функцию к строке. Результат может не быть строкой.
    indent(int x) — добавляет x пробелов в строку. Если параметр отрицателен, то это количество начальных пробелов будет удалено(если это возможно).

  2. Files — отхватил такой метод как mismatch(), который, в свою очередь, находит и возвращает позицию первого несовпадающего байта в содержимом двух файлов или -1L, если нет несоответствия.

  3. Появился новый класс — CompactNumberFormat, для форматирования десятичного числа в компактной форме. Пример такой компактной формы — 1M вместо 1000000. Таким образом, требуется всего лишь два два вместо девяти символов.

  4. Существует также новый enum, NumberFormatStyle, у которого есть два значения — LONG и SHORT.

  5. InputStream получил метод — skipNBytes(long n): пропустить n-ое количество байтов из входного потока.

Интересные ссылки по Java 12:

Java 13

Мир не стоит на месте, шевелится, развивается, как и Java — Java 13. Из 8 в 13: полный обзор версий Java. Часть 2 - 3

Text block

Java всегда немного страдала от определения строк. Если нам нужно определить строку с пробелом, перенос строки, кавычку или ещё что-то, это вызывало некоторые трудности, так приходилось использовать специальные символы: например, \n для переноса строки, или экранировать некоторые из самой строки. Это существенно снижает читаемость кода, и занимает лишнее время при написании такой строки. Эта становится особо заметным при написании строк, отображающих JSON, XML, HTML и т.д. В итоге если мы хотим написать небольшой Json, это будет выглядеть как-то так:

String JSON_STRING = "{\r\n" + "\"name\" : \"someName\",\r\n" + "\"site\" : \"https://www.someSite.com/\"\r\n" + "}";
И тут на сцену выходит Java 13 и предлагает нам свое решение в виде тройных двойных кавычек до и после текста (которые и обозвали текстовыми блоками). Давайте рассмотрим предыдущий пример json с использованием данного нововведения:

String TEXT_BLOCK_JSON = """
{
    "name" : "someName",
    "site" : "https://www.someSite.com/"
}
""";
В разы проще и наглядней, не правда ли? Также было в String было добавлено три новых метода, соответственно, для управления данными блоками:
  • stripIndent(): удаляет случайные пробелы из строки. Это полезно, если вы читаете многострочные строки и хотите применить такое же исключение случайных пробелов, как это происходит с явным объявлением (по сути имитирует компилятор для удаления случайных пробелов);
  • formatted(Object... args ): аналог format(String format, Object... arg), но для текстовых блоков;
  • translateEscapes(): возвращает строку с escape-последовательностями (например, \ r), переведенными в соответствующее значение Unicode.

Improve Switch

Выражения-переключатели были введены в Java 12, а 13 уточняет их. В 12 вы определяете возвращаемые значения с помощью break. В 13 возвращаемое значение заменили на yield. Теперь выражение со switch, которое было у нас в разделе Java 12, можно переписать как:

var result = switch (someDay) {
  case "M", "W", "F" -> "MWF";
  case "T", "TH", "S" -> "TTS";
  default -> {
      if(someDay.isEmpty())
          yield "Please insert a valid day.";
      else
          yield "Looks like a Sunday.";
  }
};
Хотя нам программистам, уже знакомым с Java, было нормально принять break, но, тем не менее, это было довольно странно. Что break true пытается мне сказать? Новое (условно новое) ключевое слово yield более понятно, и в будущем оно может появиться в других местах, где возвращаются значения. Кому глубоко интересна данная тема, рекомендую ознакомиться с данными материалами:

Dynamic CDS Archives

CDS — Class-Data Sharing. Позволяет упаковывать набор часто используемых классов в архив, который позже может быть загружен несколькими экземплярами JVM. Зачем нам это? Дело в том, что в процессе загрузки классов JVM делает довольно много ресурсозатратных действий, таких как чтение классов, сохранение их во внутренних структурах, проверка правильности прочитанных классов, поиск и загрузка зависимых классов и т. д., и лишь после всего этого классы готовы к работе. Понятное дело, попусту тратится большое количество ресурсов, ведь экземпляры JVM часто могут загружать одни и те же классы. Например String, LinckedList, Integer. Ну или же классы одного и того же приложения, а все это — ресурсы. Если бы мы выполнили все необходимые действия лишь один раз и после поместили переработанные классы в архив, который может быть подгружен в память нескольких JVM, это могло бы существенно сэкономить место в памяти и сократить время запуска приложения. Собственно, CDS дает возможность создать именно такой архив. Java 9 позволяла добавлять в архив только системные классы. Java 10 — включать в архив классы приложения. Создание такого архива состоит из:
  • создания списка классов, загружаемых приложением;
  • создания так необходимого нам архива с найденными классами.
Нововведение в Java 13 улучшает CDS так, чтобы он мог создавать архив по завершении приложения. Это означает, что два шага, приведенные выше, теперь будут объединены в один. И ещё один важный момент: только классы, которые были загружены во время работы приложения, будут добавлены в архив. Другими словами словами, те классы, которые все же содержатся в application.jar, но по каким-то причинам на были загружены, не добавятся в архив.

Update Socket API

API Socket (java.net.Socket и java.net.ServerSocket) — по сути неотъемлемая часть Java с момента ее появления, но при этом сокеты ни разу не апдейтили за последний двадцаток лет. Написанные на C и Java, они были очень и очень громоздкими и сложными в обслуживании. Но Java 13 решила внести свои коррективы в это всё дело и заменила базовую реализацию. Теперь вместо PlainSocketImpl интерфейс провайдера заменяется на NioSocketImpl. Эта новая кодированная реализация основана на той же внутренней инфраструктуре, что и java.nio. По сути класс использует механизм буферного кэша и блокировки java.util.concurrent (которые являются сегментными), а не синхронизированные методы. Он больше не требует нативного кода, тем самым упрощая портирование на разные платформы. Всё же, у нас есть способ вернуться к использованию PlainSocketImpl, но отныне по дефолту используется NioSocketImpl.

Memory Return for ZGC

Как мы помним, Z сборщик мусора ввели в Java 11 как механизм сборки мусора с малой задержкой, чтобы GC пауза никогда не превышала 10 мс. Но при этом, в отличие от остальных виртуальных GC HotSpot, таких как Shenandoah и G1, он мог возвращать неиспользованную динамическую память в ОС. Данная модификация добавляет эту возможность J в ZGC. Соответственно, мы получаем уменьшенный объем памяти вместе с улучшением производительности, и ZGC теперь возвращает не зафиксированную память операционной системе по умолчанию, пока не будет достигнут указанный минимальный размер кучи. И ещё: у ZGC теперь есть максимальный поддерживаемый размер кучи 16 ТБ. Раньше 4ТБ было пределом. Другие нововведения:
  1. javax.security — добавлено свойство jdk.sasl.disabledMechanisms для отключения механизмов SASL.
  2. java.nio — добавлен метод FileSystems.newFileSystem (Path, Map <String,?>) — соответственно, для создания нового файла.
  3. Классы java.nio теперь имеют абсолютные (в отличие от относительных) get и set-методы. Они, как и базовый абстрактный класс Buffer, включают метод slice() для извлечения части буфера.
  4. В javax.xml.parsers добавлены методы для создания экземпляров фабрик DOM и SAX (с поддержкой пространств имен).
  5. Поддержка Unicode обновлена ​​до версии 12.1.
Интересные ссылки по Java 13:

Итоги

Мы могли бы и пройтись по заявленным нововведениям в Java 14, но так как она довольно-таки скоро увидит свет — выпуск JDK 14 запланирован на 17 марта 2020 года, лучше всего будет провести отдельный, полноценный обзор на неё уже непосредственно после выхода. Еще хотелось бы обратить внимание на то, что в других языках программирования с большими перерывами между выпусками, как например в Python 2–3, нет совместимости: то есть если код написан на Python 2, нужно будет изрядно попотеть, переводя его на 3. Java — особенная в этом отношении, поскольку она чрезвычайно обратно совместима. Это означает, что ваша программа на Java 5 или 8 гарантированно будет работать с виртуальной машиной Java 8-13 — с некоторыми исключениями, о которых вам сейчас не нужно беспокоиться. Понятное дело, что это не работает наоборот: например, если ваше приложение юзает функции Java 13, которые просто недоступны в Java 8 JVM. На этом у меня сегодня всё, тем кто дочитал до этого места — респект)) Из 8 в 13: полный обзор версий Java. Часть 2 - 5
Комментарии (5)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Бурцев Юрий Уровень 12
23 февраля 2020
Спасибо. В последнее время статьи становятся всё интереснее и интереснее! Привет всем из Питера.
Василий Петров Уровень 37
19 февраля 2020
А по содержанию, такое мнение, что оракл решил каждые полгода выпускать новую альфабетагамму версию очередного сборщика мусора и говорить что это крутое движение языка вперед. А в реальной жизни все как сидели на 8, а особо упоротые влезли на 11, так и будут дальше сидеть.
Василий Петров Уровень 37
19 февраля 2020
Местами гуглотранслейтостудентоперевод, лучше бы прямо исходный английский текст публиковали и постепенно нормально переводили кусками.
Prolemey Уровень 4
19 февраля 2020
Ничего не понял, но очень интересно D Евгений 2 уровень
Роман Уровень 24
18 февраля 2020
Братик это тебе респект