Причины перехода на Java 11 и более

Вопрос заключается не в том, следует ли перейти на Java 11 или более позднюю версию, а когда. В течение следующих нескольких лет Java 8 больше не будет поддерживаться, и пользователям придется перейти на Java 11 или более поздней версии. Мы уверены, что переход на Java 11 дает весомые преимущества, и рекомендуем всем разработчикам выполнить его как можно скорее.

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

Переход на Java 11

Переход на Java 11 можно выполнять поэтапно. Для работы с Java 11 вам не нужно использовать в коде специальные модули Java. Java 11 можно использовать для запуска кода, который был разработан и скомпилирован в JDK 8. Но при этом могут возникать некоторые проблемы, которые преимущественно связаны с устаревшими API, загрузчиками классов и отражениями.

Группа разработки на Java корпорации Майкрософт имеет инструкции по переходу с Java 8 на Java 11. Также могут быть полезны руководства Платформа Java, руководство по миграции для выпуска Standard Oracle JDK 9 и Состояние системы модулей: совместимость и миграция.

Обобщенное описание отличий между Java 8 и 11

В этом разделе не перечисляются все изменения, внесенные в Java версии 9 [1], 10 [2], и 11 [3]. Выделены лишь те из них, которые заметно влияют на эффективность, диагностику и производительность.

Модули [4]

Модули устраняют проблемы с конфигурацией и инкапсуляцией, которые трудно отслеживать в крупномасштабных приложениях на основе classpath. Модуль — это самоописывающая коллекция классов и интерфейсов Java со всеми связанными ресурсами.

Модули позволяют настраивать конфигурации среды выполнения, которые содержат только необходимые для приложения компоненты. Это помогает сократить потребление памяти, позволяя приложению использовать статическое связывание с jlink в пользовательской среде выполнения для развертывания. Сниженное потребление памяти особенно полезно при использовании архитектуры микрослужб.

На внутреннем уровне виртуальная машина Java может применять модули для повышения эффективности загрузки классов. Благодаря этому среда выполнения будет меньше, легче и быстрее. Методы оптимизации, которые виртуальная машина Java использует для повышения производительности, могут оказаться более эффективными, так как модули кодируют компоненты, требуемые для класса.

Модули помогают программистам обеспечить строгую инкапсуляцию, так как требуют явного объявления экспортируемых модулем пакетов и необходимых компонентов, а также ограничивают отражающий доступ. Такой уровень инкапсуляции делает приложение более надежным и простым в обслуживании.

В приложении можно как и прежде использовать classpath. При этом вам не нужно переходить на использование модулей работы с Java 11.

Профилирование и диагностика

Летчик Java [5]

Java Flight Recorder собирает данные диагностики и профилирования из запущенного приложения Java. Java Flight Recorder практически не влияет на работу приложения Java. Собранные данные потом можно проанализировать с помощью Java Mission Control или других средств. В Java 8 Java Flight Recorder и Java Mission Control предоставлялись на коммерческой основе, а в Java 11 они включены как компоненты с открытым кодом.

Управление миссиями Java [6]

Java Mission Control (JMC) предоставляет графическое отображение данных, собранных Java Flight Recorder (JFR) и открытый код в Java 11. Помимо общих сведений о работающем приложении JMC позволяет пользователю детализировать данные. Java Flight Recorder и Java Mission Control можно использовать для диагностики проблем, возникающих во время выполнения, включая утечки памяти, затраты на сборку мусора, горячие методы, узкие места и блокирующие операции ввода-вывода.

Единое ведение журнала [7]

В Java 11 организована централизованная система ведения журналов для всех компонентов виртуальной машины Java. Эта система позволяет пользователю выбирать компоненты и уровни детализации для регистрации. Такое детализированное ведение журналов полезно при анализе причин сбоев виртуальной машины Java и диагностики проблем с производительностью в рабочей среде.

Профилирование кучи с низкими затратами [8]

В виртуальную машину Java добавлен новый API, который предназначен для выборки выделений памяти в куче Java. Эта выборка создает низкую нагрузку и ее можно не отключать. Хотя выделение кучи можно отслеживать с помощью Java Flight Recorder, используемый метод выборки работает только для выделений. Кроме того, эта реализация может упускать выделения памяти. При этом выборка кучи в Java 11 предоставляет сведения как об активных, так и о неработающих объектах.

Поставщики средств наблюдения за производительностью приложений уже применяют эту новую функцию, и инженерная группа Java оценивает возможность объединить ее со средствами мониторинга производительности Azure.

StackWalker [9]

Получение моментального снимка стека в текущем потоке будет очень полезным при ведении журналов. Основная проблема заключается в том, чтобы выбрать объем трассировки стека для записи в журнал, а также принять решение о записи трассировки стека. Например, вам нужно отслеживать трассировку стека только для определенного исключения в одном методе. Класс StackWalker (добавлен в Java 9) предоставляет моментальный снимок стека и несколько методов, которые позволяют программисту контролировать использование трассировки стека.

Сборка мусора [10]

В Java 11 доступны следующие сборщики мусора: последовательные, параллельные, Garbage-First и Epsilon. По умолчанию в Java 11 используется сборщик мусора G1GC (Garbage First Garbage Collector).

Остальные здесь упоминаются для полноты информации. Сборщик мусора ZGC (Z Garbage Collector) нацелен на параллельное выполнение с низкой задержкой — по возможности не более 10 мс. ZGC используется в Java 11 в качестве экспериментальной функции. Сборщик мусора Shenandoah рассчитан на краткую приостановку, которой он добивается за счет выполнения большего количества сборок мусора параллельно с запущенной программой Java. Shenandoah предоставляется в качестве экспериментальной функции в Java 12, но существуют реализации с обратной совместимостью с Java 11. Доступен также сборщик CMS (Concurrent Mark and Sweep), но, начиная с версии Java 9, он объявлен нерекомендуемым.

В виртуальной машине Java по умолчанию назначается оптимальный сборщик мусора для усредненного сценария использования. Эти и другие параметры по умолчанию для сборки мусора иногда приходится изменять, чтобы добиться оптимальной пропускной способности или задержки в соответствии с требованиями приложения. Для правильной настройки сборщика мусора нужно хорошо разбираться в принципах его работы. В этом вам может помочь группа инженеров Microsoft Java.

G1GC

По умолчанию в Java 11 используется сборщик мусора G1 (G1 Garbage Collector). Задача G1GC — найти баланс между задержкой и пропускной способностью. Сборщик мусора G1 пытается достичь высокой пропускной способности, с высокой вероятностью обеспечивая заданное значение приостановки. G1GC предназначен для предотвращения полных коллекций, но когда параллельные коллекции не могут освободить память достаточно быстро, будет выполнена резервная сборка мусора. При полной сборке мусора используется такое же количество параллельных рабочих потоков, что и при сборки молодого и смешанного мусора.

Сборщик мусора Parallel

Сборщик Parallel используется по умолчанию в Java 8. Сборщик мусора Parallel нацелен на обеспечение высокой пропускной способности при использовании нескольких потоков для ускорения сборки.

Epsilon [11]

Сборщик мусора Epsilon обрабатывает выделения, но не освобождает память. Когда куча будет исчерпана, виртуальная машина Java завершит работу. Epsilon удобно использовать для служб с коротким сроком жизни и приложений, которым гарантированно не требуется сборка мусора.

Улучшения для контейнеров Docker [12]

До версии Java 10 в виртуальной машине Java не учитывались ограничения памяти и ЦП, заданные для контейнера. Например, в Java 8 виртуальная машина Java по умолчанию ограничивала размер кучи на уровне 1/4 от общего объема физической памяти базового узла. Начиная с Java 10, в виртуальной машине Java учитываются ограничения, заданные для групп управления контейнерами (cgroup), при установке ограничений памяти и ЦП (см. примечание ниже). Например, максимальный размер кучи по умолчанию составляет 1/4 от ограничения памяти для контейнера (500 МБ при использовании -m2G).

Также добавлены новые параметры виртуальной машины Java. Они позволяют пользователям контейнеров Docker контролировать объем системной памяти, который будет использоваться для кучи Java.

Эта возможность включена по умолчанию, но доступна только на платформах под управлением Linux.

Примечание

Большая часть работы по поддержке cgroup направлена на обеспечение обратной совместимости в Java 8, начиная с версии jdk8u191. Мы не гарантируем, что последующие улучшения будут доступны в версии 8.

Jar-файлы с несколькими выпусками [13]

В Java 11 можно создать JAR-файл, который содержит несколько версий файлов классов, предназначенных для конкретных выпусков Java. Такие JAR-файлы позволяют разработчикам библиотек поддерживать несколько версий Java без необходимости предоставлять несколько версий JAR-файлов. Для потребителей этих библиотек такие JAR-файлы решают проблему подбора правильных JAR-файлов для каждой целевой версии среды выполнения.

Разные улучшения производительности

Следующие изменения оказывают прямое влияние на производительность виртуальной машины Java.

  • JEP 197: сегментированные кэши кода [14] — делит кэш кода на отдельные сегменты. Такая сегментация позволяет управлять объемом памяти, используемой виртуальной машиной Java, ускоряет сканирование скомпилированных методов, значительно снижает фрагментацию кэша кода и повышает производительность.

  • JEP 254: компактные строки [15] — изменяет внутреннее представление строки с двух байтов на char на один или два байта на char в зависимости от кодировки char. Так как большинство строк содержат символы кодировки ISO-8859-1/Latin-1, это изменение вдвое сокращает пространство, необходимое для хранения строк.

  • JEP 310: совместное использование приложений Class-Data [16] — Class-Data общий доступ уменьшает время запуска, позволяя архивным классам сопоставляться с памятью во время выполнения. а также расширяет возможность совместного использования данных класса, позволяя размещать классы приложений в архив Common Data Service. Если несколько виртуальных машин Java совместно используют один и тот же файл архива, потребление памяти и общее время отклика системы снижаются.

  • JEP 312: Thread-Local подтверждения [17] — позволяет выполнять обратный вызов в потоках без выполнения глобальной безопасной точки виртуальной машины, что помогает виртуальной машине достичь меньшей задержки за счет уменьшения числа глобальных безопасных точек.

  • Отложенное выделение потоков компилятора [18] — в многоуровневом режиме компиляции виртуальная машина запускает большое количество потоков компилятора. Этот режим используется по умолчанию в системах с большим количеством процессоров. Такие потоки создаются независимо от объема доступной памяти или количества запросов компиляции. Потоки потребляют память, даже когда бездействуют (что происходит почти все время), что приводит к неэффективному использованию ресурсов. Для решения этой проблемы реализация изменена так, чтобы при запуске открывался только один поток компилятора каждого типа. Запуск дополнительных потоков и завершение работы неиспользуемых потоков осуществляется динамически.

Следующие изменения в основных библиотеках влияют на производительность нового или измененного кода.

  • JEP 193: дескриптора переменных [19] — определяет стандартный способ вызова эквивалентов различных операций java.util.concurrent.atomic и sun.misc.Unsafe при полях объектов и элементах массива, стандартный набор операций ограждения для точного управления упорядочением памяти, а также стандартная операция обеспечения доступности и ограждения для обеспечения строгой доступности объекта.

  • JEP 269: удобные фабричные методы для коллекций [20] — определяет API библиотеки, чтобы упростить создание экземпляров коллекций и карт с небольшим количеством элементов. Эти статические фабричные методы в интерфейсах коллекции создают компактный и неизменяемый экземпляр коллекции. Такие экземпляры по своей сути являются более эффективными. API создают коллекции, которые хранятся в компактном виде и не имеют класс-оболочку.

  • JEP 285: Spin-Wait подсказки [21] — предоставляет API, который позволяет Java намекать системе времени выполнения, что она находится в цикле спина. Некоторые аппаратные платформы могут использовать информацию от программного обеспечения о том, что поток находится в состоянии активного ожидания.

  • JEP 321: HTTP-клиент (стандартная версия) [22]. Предоставляет новый API клиента HTTP, который реализует HTTP/2 и WebSocket и может заменить устаревший API HttpURLConnection.

Ссылки

[1] Oracle Corporation, "Заметки о выпуске Пакета средств разработки Java 9", (Online). Доступно: https://www.oracle.com/technetwork/java/javase/9u-relnotes-3704429.html. (Проверено: 13 ноября 2019 г.).

[2] Oracle Corporation, "Заметки о выпуске пакета средств разработки Java 10", (Online). Доступно: https://www.oracle.com/technetwork/java/javase/10u-relnotes-4108739.html. (Проверено: 13 ноября 2019 г.).

[3] Oracle Corporation, "Заметки о выпуске пакета средств разработки Java 11", (Online). Доступно: https://www.oracle.com/technetwork/java/javase/11u-relnotes-5093844.html. (Проверено: 13 ноября 2019 г.).

[4] Oracle Corporation, "Project Jigsaw", 22 сентября 2017 г. (в Сети). Доступно: http://openjdk.java.net/projects/jigsaw/. (Проверено: 13 ноября 2019 г.).

[5] Oracle Corporation, "JEP 328: Flight Recorder", 9 сентября 2018 г. (в сети). Доступно: http://openjdk.java.net/jeps/328. (Проверено: 13 ноября 2019 г.).

[6] Oracle Corporation, "Mission Control", 25 апреля 2019 г. (в сети). Доступно: https://wiki.openjdk.java.net/display/jmc/Main. (Проверено: 13 ноября 2019 г.).

[7] Oracle Corporation, "JEP 158: Unified JVM Logging", 14 февраля 2019 г. (в сети). Доступно: http://openjdk.java.net/jeps/158. (Проверено: 13 ноября 2019 г.).

[8] Oracle Corporation, "JEP 331: Low-Overhead Профилирование кучи", 5 сентября 2018 г. (в сети). Доступно: http://openjdk.java.net/jeps/331. (Проверено: 13 ноября 2019 г.).

[9] Oracle Corporation, "JEP 259: Stack-Walking API", 18 июля 2017 г. (в сети). Доступно: http://openjdk.java.net/jeps/259. (Проверено: 13 ноября 2019 г.).

[10] Oracle Corporation, "JEP 248: Make G1 the Default Garbage Collector", 12 сентября 2017 г. (в сети). Доступно: http://openjdk.java.net/jeps/248. (Проверено: 13 ноября 2019 г.).

[11] Oracle Corporation, "JEP 318: Epsilon: A No-Op сборщик мусора", 24 сентября 2018 г. (в сети). Доступно: http://openjdk.java.net/jeps/318. (Проверено: 13 ноября 2019 г.).

[12] Oracle Corporation, "JDK-8146115: улучшение обнаружения контейнеров Docker и использования конфигурации ресурсов", 16 сентября 2019 г. (в сети). Доступно: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115. (Проверено: 13 ноября 2019 г.).

[13] Oracle Corporation, "JEP 238: Multi-Release JAR Files", 22 июня 2017 г. (в сети). Доступно: http://openjdk.java.net/jeps/238. (Проверено: 13 ноября 2019 г.).

[14] Oracle Corporation, "JEP 197: сегментированные кэши кода", 28 апреля 2017 г. (в сети). Доступно: http://openjdk.java.net/jeps/197. (Проверено: 13 ноября 2019 г.).

[15] Oracle Corporation, "JEP 254: Compact Strings", 18 мая 2019 г. (в сети). Доступно: http://openjdk.java.net/jeps/254. (Проверено: 13 ноября 2019 г.).

[16] Oracle Corporation, "JEP 310: application Class-Data Sharing", 17 августа 2018 г. (в сети). Доступно: https://openjdk.java.net/jeps/310. (Проверено: 13 ноября 2019 г.).

[17] Oracle Corporation, "JEP 312: Thread-Local рукопожатий", 21 августа 2019 г. (в сети). Доступно: https://openjdk.java.net/jeps/312. (Проверено: 13 ноября 2019 г.).

[18] Oracle Corporation, "JDK-8198756: отложенное выделение потоков компилятора", 29 октября 2018 г. (в сети). Доступно: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8198756. (Проверено: 13 ноября 2019 г.).

[19] Oracle Corporation, "JEP 193: переменные дескриптора", 17 августа 2017 г. (в сети). Доступно: https://openjdk.java.net/jeps/193. (Проверено: 13 ноября 2019 г.).

[20] Oracle Corporation, "JEP 269: Удобные фабричные методы для коллекций", 26 июня 2017 г. (в сети). Доступно: https://openjdk.java.net/jeps/269. (Проверено: 13 ноября 2019 г.).

[21] Oracle Corporation, "JEP 285: Spin-Wait Hints", 20 августа 2017 г. (в сети). Доступно: https://openjdk.java.net/jeps/285. (Проверено: 13 ноября 2019 г.).

[22] Oracle Corporation, "JEP 321: HTTP Client (Standard)," 27 сентября 2018 г. (в сети). Доступно: https://openjdk.java.net/jeps/321. (Проверено: 13 ноября 2019 г.).