Причины перехода на 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 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.
Что такое java 11
С установленным JDK вашего любимого поставщика (и, возможно, с LTS в заднем кармане) вы хотите чтобы ваш проект на Java 8 заработал на Java 11. Мы знаем, что вы готовы к апгрейду, но прежде, чем мы пойдем дальше, необходимо обсудить, как подготовиться к переходу наилучшим образом.
Быстрый или длительный переход?
Перед началом перехода с Java 8 на Java 11 или на более позднюю версию, первый вопрос, на который необходимо ответить — можете ли вы реализовать переход одним махом или вам необходимо провести его в течение длительного промежутка времени.
Если миграция обещает быть быстрой или к проекту могут быть предъявлены новые требования по использованию версии Java, используйте новую версию Java сразу для всего процесса сборки, включая компиляцию. Осталось только исправить проблемы, которые при этом могут возникнуть.
Если же вы не хотите изменять требования к минимальной версии Java или ваш проект слишком большой, мы рекомендуем следующий подход:
- Не создавайте отдельную долговременную ветку для перехода — вместо этого делайте это в регулярной ветке развития. Таким образом, вы не столкнетесь с конфликтами при слиянии и можете быть уверены, что изменения ваших коллег также построены на Java 11.
- Настройте ваш сервер непрерывной интеграции для запуска сборок проекта как в своей общей конфигурации, так и на Java 11.
- Изучите, как ваша система сборки может поддерживать различные конфигурации, зависящие от версии Java (в Maven для этого используются профайлы). Таким образом, вы можете сохранить старую версию сборки, добавив одновременно конфигурацию, которая использует новую версию.
- Постарайтесь сократить изменения в конфигурации, зависящие от версии Java до минимума.
Таким образом, вы можете гарантировать, что ваш проект работает и на Java 8 и на Java 11 (или более поздней версии). Если вы захотите работать с двумя (или более) версиями Java, то сможете быстро переключать профайлы и работать с ними. А когда вы совсем перестанете использовать Java 8, то не забудьте объединить версии конфигурации, чтобы уменьшить сложность проекта.
Делаем общий апгрейд
Первое правило перехода на Java 11 — обновить все: вашу IDE, систему сборки, ее плагины и, самое главное, ваши зависимости. Вам не обязательно делать все эти обновления заранее, но если вы это можете, то вы должны так сделать — это, скорее всего, поможет вам преодолеть некоторые препятствия, относительно которых вы останетесь в блаженном неведении.
Ниже приведены необходимые минимальные версии для некоторых приложений:
Некоторые зависимости, которые вы должны отслеживать (а также версии, которые, как известно, работают на Java 11):
- Все, что использует для работы байт-код, например ASM (7.0), Byte Buddy (1.9.0), cglib (3.2.8) или Javassist (3.23.1-GA). Начиная с Java 9, модификации байт-кода происходят каждые шесть месяцев, поэтому вам придется регулярно обновлять такие библиотеки.
- Все, что использует то, что работает с байт-кодом, например Spring (5.1), Hibernate (неизвестно), Mockito (2.20.0) и многие, многие другие проекты.
Второй совет слишком общий и не очень-то помогает, но это печальная правда: многие мощные проекты работают с байт-кодом под капотом. Это помогает развить подход к выявлению проблем, связанных с этим.
Вот некоторые советы — обращайте внимание в логах на:
- трассировки стеков, которые заканчиваются вызовом библиотек, использующих байт-коды;
- ошибки или предупреждения, которые жалуются на уровень байт-кода;
- ошибки или предупреждения, которые бормочут о «unknown (constant pool) entries»;
Если вы не обновились заранее, то обновление библиотек и фреймворков все равно будет первым действием, которое вы предпримите, столкнувшись с проблемой с каким-либо конкретным инструментом или зависимостью. В любом случае, вы можете иногда сталкиваться с проблемами, даже если ваши зависимости были обновлены. В этом случае взгляните на точный артефакт, вызывающий проблему — скорее всего, это транзитивная зависимость, и в этом случае вы должны изучить ее отдельно.
Например, для более старой версией Hibernate необходимо было обновить Javassist для работы с Java 11:
org.hibernate hibernate-core 5.2.12.Final org.javassist javassist 3.23.1-GA
Аналогично, при использовании устаревшей версии Maven-плагина 3.7.0 для компиляции, необходимо было обновление его зависимости от ASM:
org.apache.maven.plugins maven-compiler-plugin 3.7.0 $ org.ow2.asm asm 6.2
К сожалению, не у всех, используемых в вашем проекте библиотек, поддержка может находиться в хорошем состоянии и даже, возможно, что она прекращена. В этом случае вам нужно искать альтернативы. Примерами могут являтся FindBugs (вместо этого используйте SpotBugs), Log4j 1 (используйте Log4J 2) и Cobertura (используйте JaCoCo).
Однако, если проблема кроется в вашем собственном коде или такие обновления / замены не помогают или невозможны, тогда имеет смысл заняться углубленным ее изучением.
Что такое java 11
26 сентября, 2018
Только что официально вышла новая, одиннадцатая версия Java. Скачать образы JDK можно либо в виде Oracle JDK под коммерческой лицензией, либо как OpenJDK с лицензией GPLv2. Различия между ними заключаются в том, что бесплатную версию Oracle JDK 11 можно легально использовать только для разработки, а OpenJDK можно использовать и в производственном целях.
Образы JRE не предоставляются. Для создания JRE пользователи теперь должны сами запускать утилиту jlink .
Java 11, как и Java 10, это достаточно маленький (по количеству изменений) релиз, потому что после выхода предыдущей версии прошло всего полгода. Однако, в отличие от Java 10, этот релиз является Long Term Support, а значит, он будет поддерживаться как минимум до сентября 2022 года. К сожалению, бесплатные обновления Oracle будет выпускать только в течение полугода. Для получения дальнейших обновлений придётся либо заплатить, либо использовать альтернативные источники поддержки, например, AdoptOpenJDK, RedHat, Azul и т.д.
Вот список того, что было изменено и улучшено в Java 11:
- Удалены аплеты и Web Start, которые ранее в Java 9 были помечены как deprecated.
- JavaFX и Java Misssion Control теперь не поставляются вместе с JDK, а существуют как отдельные проекты.
- На Windows и macOS теперь нет автообновлений.
- Формат поставки для Windows сменился с tar.gz на zip.
- JEP 323: Local-Variable Syntax for Lambda Parameters. Теперь ключевое слово var можно использовать не только для объявления локальных переменных методов, но и для параметров лямбда-выражений.
- JEP 320: Remove the Java EE and CORBA Modules. Модули JAX-WS, JAXB, JAF, Common Annotations, CORBA, JTA, которые ранее в Java 9 были помечены как deprecated for removal, теперь удалены окончательно. Вместо них теперь нужно использовать их аналоги из Maven. Также удалён модуль-аггрегатор java.se.ee .
- JEP 321: HTTP Client (Standard). HTTP Client, который появился в Java 9 как инкубационный модуль, теперь стал стандартным модулем.
- JEP 328: Flight Recorder. Утилита Flight Recorder для диагностики и профилирования Java-приложений теперь стала общедоступной частью OpenJDK. Раньше её можно было использовать только на коммерческой основе.
- JEP 330: Launch Single-File Source-Code Programs. Исходные файлы Java теперь можно исполнять напрямую, без промежуточной компиляции.
- JEP 181: Nest-Based Access Control. Доступ между закрытыми членами вложенных классов теперь реализован на уровне JVM, а значит больше не нужна генерация синтетических методов доступа между ними.
- JEP 309: Dynamic Class-File Constants. Появился новый механизм constantdynamic ( condy ), благодаря которому константными значениями теперь могут быть не только простые литералы, а более сложные динамические выражения произвольного типа.
- JEP 318: Epsilon: A No-Op Garbage Collector. Появился новый сборщик мусора EpsilonGC, который занимается только выделением памяти, но не освобождает её. При достижении лимита памяти виртуальная машина останавливается.
- JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental). Появился новый сборщик мусора ZGC, который ставит своей задачей обеспечить маленькие паузы (
Подписывайтесь на канал в Telegram, чтобы не пропускать новости.
- Вышла Java 21
- Новости Java #57
- Новости Java #56
- Вышла Java 20
- Новости Java #55
- Новости Java #54
- Новости Java #53
- Новости Java #52
- Новости Java #51
- Новости Java #50
- Новости Java #49
- Новости Java #48
- Новости Java #47
- Новости Java #46
- Новости Java #45
- Вышла Java 19
- Новости Java #44
- Новости Java #43
- Новости Java #42
- Новости Java #41
- Новости Java #40
- Новости Java #39
- Новости Java #38
- Новости Java #37
- Вышла Java 18
- Новости Java #36
- Новости Java #35
- Новости Java #34
- Новости Java #33
- Новости Java #32
- Новости Java #31
- Новости Java #30
- Новости Java #29
- Новости Java #28
- Вышла Java 17
- Новости Java #27
- Новости Java #26
- Новости Java #25
- Новости Java #24
- Новости Java #23
- Новости Java #22
- Новости Java #21
- Новости Java #20
- Новости Java #19
- Новости Java #18
- Новости Java #17
- Новости Java #16
- Новости Java #15
- API, ради которых наконец-то стоит обновиться с Java 8. Часть 3
- Новости Java #14
- Новости Java #13
- Новости Java #12
- Новости Java #11
- Вышла Java 16
- Новости Java #10
- Новости Java #9
- Новости Java #8
- Новости Java #7
- Новости Java #6
- Новости Java #5
- Новости Java #4
- Новости Java #3
- Новости Java #2
- Новости Java #1
- Вышла Java 15
- Исследуем sealed классы в Java 15
- Java исполняется 25 лет
- В Java можно будет объявлять локальные интерфейсы и перечисления
- В Java появятся паттерны деконструкции
- Вышла Java 14
- Из Java исчезнет Nashorn
- API, ради которых наконец-то стоит обновиться с Java 8. Часть 2
- API, ради которых наконец-то стоит обновиться с Java 8. Часть 1
- В Java появятся скрытые классы
- У miniJUG появился сайт
- Исследуем записи в Java 14
- Пробуем улучшенный оператор instanceof в Java 14
- Вышла Java 13
- В Java появятся две новые экранирующие последовательности для продолжения строки и пробела
- В Java может появиться новая сериализация
- Вышла Scala 2.13
- В switch -выражениях break будет заменён на yield
- В Java появятся блоки текста
- В Java появятся записи и запечатанные типы
- Oracle прекращает поддержку Java
- В Java у NullPointerException могут появиться детальные сообщения
- Вышла Java 12
- Вышла Java 11
- В Java появятся компактные тела методов
- Вышла Java 10
- Oracle JDK станет платным
- В Java можно будет исполнять исходные файлы напрямую
- В Java могут появиться сырые строковые литералы, и какие проблемы это может принести
- В Java 10 будет улучшена поддержка неизменяемых коллекций
- switch в Java сможет возвращать значения
- В конструкторах классов можно будет писать код перед this() и super()
- В лямбдах можно будет использовать var
- В Java появится стандартный HTTP-клиент
- В Java исчезнет необходимость в генерации синтетических методов доступа между вложенными типами
- Модули Java EE и CORBA исчезнут из Java
- Вышел Kotlin 1.2
- В Java появится сборщик мусора, который не будет собирать мусор
- Стала известна дата выхода Java 10
- В Java появится паттерн-матчинг
- Егор Бугаенко раскритиковал идею введения data-классов в Java
- Конструкторы Integer, Long, Float, Double, Boolean, Byte, Short, Character стали deprecated в Java 9
- Ранние сборки JDK 10 уже доступны для скачивания
- В Java появятся data-классы
- Java всё-таки не будет версионироваться годом и месяцем выхода релизов
- Java переходит на 6-месячный релизный цикл и меняет схему версионирования
- В Java появятся легковесные нити и корутины
- В Oracle разрабатывают новый сборщик мусора ZGC
90 новых фич (и API) в JDK 11
Привет, Хабр! Представляю вашему вниманию перевод статьи «90 New Features (and APIs) in JDK 11» от автора Simon Ritter.

Новый шестимесячный релизный цикл JDK для многих означает, что некоторые ещё даже не выяснили, какие новые функции в JDK 10, а на пороге уже JDK 11. В одном из ранних блогов (англ.), были перечислены все 109 новых фич и API, которые удалось найти в JDK 10. Поэтому для JDK 11 было решено поступить аналогично. Тем не менее, был выбран другой формат. Этот пост будет поделён на два раздела: новые фичи, которые доступны разработчикам (публичный API) и всё остальное. Таким образом, если вас интересует только то, что непосредственно повлияет на вашу разработку, вы можете пропустить вторую часть.
Общее число изменений, которое удалось подсчитать, получилось равным 90 (это JEP плюс новые классы и методы, исключая отдельные методы для HTTP-клиента и Flight Recorder) (прим. переводчика: Java Flight Recorder (JFR) был одним из коммерческих дополнений от Оракла встроенным в JDK, но начиная с Java 11, благодаря JEP 328, был передан в опенсорс). Хоть и в JDK 11 удалось найти на одиннадцать изменений меньше, чем в JDK 10, считаю, что справедливо сказать, что в JDK 11 добавлено больше функциональных возможностей, однозначно на уровне JVM.
Новые заметные для разработчика фичи
В JDK 11 довольно мало изменений, которые могли бы повлияет на стиль разработки. Присутствует небольшое изменение синтаксиса, множество новых API и возможность запуска приложений одним файлом без использования компилятора (прим. переводчика: так называемые shebang файлы). Кроме того, большим (и ломающим) изменением является удаление агрегирующего модуля java.se.ee, что может повлиять на перенос существующего приложения на JDK 11.
В JDK 10 был введен вывод локальных переменных (или вывод типов) (JEP 286). Это упрощает код, поскольку вам больше не нужно явно указывать тип локальной переменной, вместо этого можно использовать var. JEP 323 расширяет использование этого синтаксиса, который теперь также применим к параметрам лямбда-выражений. Простой пример:
list.stream() .map((var s) -> s.toLowerCase()) .collect(Collectors.toList());
Внимательный программист на Java указал бы, что лямбда-выражения уже имеют вывод типа, поэтому использование var было бы (в данном случае) излишним. Мы могли бы так же легко написать тот же код, что и:
list.stream() .map(s -> s.toLowerCase()) .collect(Collectors.toList());
Зачем было нужно добавлять поддержку var? Ответом является один особый случай — когда вы хотите добавить аннотацию к лямбда-параметру. Это невозможно сделать без участия какого-либо типа. Чтобы избежать использования явного типа, мы можем использовать var для упрощения вещей, таким образом:
list.stream() .map((@Notnull var s) -> s.toLowerCase()) .collect(Collectors.toList());
Это изменение потребовало изменений в спецификации языка Java (JLS), в частности:
Страница 24: The description of the var special identifier.
Страница 627-630: Lambda parameters
Страница 636: Runtime evaluation of Lambda expressions
Страница 746: Lambda syntax
Одним из критических замечаний к Java является избыточность синтаксиса, а «церемония», связанная с запуском даже тривиального приложения, может серьёзно повысить порог входа для новичка. Для написания приложения, которое просто печатает «Hello World!», требуется написать класс с общедоступным статическим void main методом и использовать метод System.out.println(). Сделав это, вы должны скомпилировать код с помощью javac. Наконец, вы cможете запустить приложение, которое будет приветствовать мир. Выполнение того же самого сценария, в большинстве современных языков значительно проще и быстрее.
JEP 330 устраняет необходимость компиляции однофайлового приложения. Теперь достаточно ввести:
java HelloWorld.java
Java лаунчер идентифицирует, что файл содержит исходный код Java и скомпилирует код в *.class файл перед его выполнением.
Аргументы, помещенные после имени исходного файла, передаются в качестве аргументов при запуске приложения. Аргументы, помещенные перед именем исходного файла, передаются в качестве аргументов java лаунчеру после компиляции кода (это позволяет задавать такие вещи, как classpath, в командной строке). Аргументы, относящиеся к компилятору (например, путь к классам), также будут переданы в javac для компиляции.
java -classpath /home/foo/java Hello.java Bonjour
javac -classpath /home/foo/java Hello.java java -classpath /home/foo/java Hello Bonjour
Этот JEP также обеспечивает поддержку «shebang» файлов. Чтобы уменьшить необходимость даже упоминать java лаунчер в командной строке, можно его включить в первую строку исходного файла. Например:
#!/usr/bin/java --source 11 public class HelloWorld < .
Флаг -source с используемой версией Java является обязательным.
JDK 9 предоставила новый API для поддержки протокола HTTP Client (JEP 110). Так как JDK 9 предоставила Java Platform Module System (JPMS), этот API был включен как модуль инкубатора. Модули инкубаторов предназначены для предоставления новых API, но не превращают их в стандарт Java SE. Разработчики могут попробовать API, предоставив обратную связь. После внесения необходимых изменений (этот API был обновлен в JDK 10), API можно перенести в основной модуль, чтобы стать частью стандарта.
API HTTP Client теперь является частью стандарта Java SE 11. Это вводит новый модуль и пакет для JDK, java.net.http. Основные классы:
API можно использовать синхронно или асинхронно. В асинхронном режиме используются CompletionFutures и CompletionStages.
С введением JPMS в JDK 9 можно было разделить монолитный файл rt.jar на несколько модулей. Дополнительным преимуществом JPMS является то, что теперь можно создать среду выполнения Java, которая включает только модули, необходимые для вашего приложения, значительно уменьшая общий размер. Имея явно определенные границы, устаревшие модули теперь проще удалять из Java API. Это то, что делает этот JEP; метамодуль java.se.ee включает в себя шесть модулей, которые больше не будут частью стандарта Java SE 11 и не будут включены в JDK.
- corba (прим. переводчика: rest in peace , burn in hell)
- transaction
- activation
- xml.bind
- xml.ws
- xml.ws.annotation
Эти модули были помечены устаревшими (@Deprecated) начиная с JDK 9 и не были включены по умолчанию в компиляцию или рантайм. Если вы пытались скомпилировать или запустить приложение, использующее API из этих модулей на JDK 9 или JDK 10, то потерпели бы неудачу. Если вы используете API из этих модулей в своем коде, вам нужно будет предоставить их в виде отдельного модуля или библиотеки. Судя по отзывам кажется, что модули java.xml, которые являются частью поддержки веб-сервисов JAX-WS, SOAP, — это те, которые вызовут наибольшее количество проблем.
Новый публичный API
Многие новые API-интерфейсы в JDK 11 являются результатом того, что клиентский модуль HTTP теперь является частью стандарта, а также включение Flight Recorder.
Полный схематичный список изменений API, включая сравнение различных версий JDK, можно посмотреть здесь.
Здесь перечислены все новые методы, отличные от тех, которые содержатся в модулях java.net.http и jdk.jfr. Также не перечислены новые методы и классы в модулях java.security, которые довольно специфичны для изменений JEP 324 и JEP 329 (есть шесть новых классов и восемь новых методов).
java.io.ByteArrayOutputStream
- void writeBytes(byte []): записывает все байты из аргумента в OutputStream
java.io.FileReader
Два новых конструктора, которые позволяют указать Charset.
java.io.FileWriter
Четыре новых конструктора, которые позволяют указать Charset.
java.io.InputStream
- io.InputStream nullInputStream(): возвращает InputStream, который не считывает байты. Взглянув на этот метод (и на тот, что в OutputStream, Reader и Writer), возникает вопрос, для чего он может пригодиться. Можно думать о них как о /dev/null — для выброса вывода, который вам не нужен, или предоставления ввода, который всегда возвращает нулевые байты.
java.io.OutputStream
java.io.Reader
java.io.Writer
java.lang.Character
- String toString(int): это перегруженная форма существующего метода, но вместо char используется int. Int — кодовая точка Unicode.
java.lang.CharSequence
- int compare(CharSequence, CharSequence): сравнивает два экземпляра CharSequence лексикографически. Возвращает отрицательное значение, ноль или положительное значение, если первая последовательность лексикографически меньше, равна или больше второй, соответственно.
java.lang.ref.Reference
- lang.Object clone(): Должен признаться, это изменение вызывает непонимание. Класс Reference не реализует интерфейс Cloneable, и этот метод выбрасывает исключение CloneNotSupportedException. Должна быть причина для его включения, возможно, для чего-то в будущем. (прим. переводчика: есть обсуждение на StackOverflow, тикет в OpenJDK)
java.lang.Runtime
java.lang.System
Здесь нет новых методов здесь, но стоит упомянуть, что метод runFinalizersOnExit() теперь удален из обоих классов (может возникнуть проблема при миграции на JDK 11).
java.lang.String
Я думаю, что это один из основных моментов новых API в JDK 11. Здесь есть несколько полезных новых методов.
- boolean isBlank(): возвращает true, если строка пуста или содержит только пробелы, иначе false.
- Stream lines(): возвращает Stream из String, извлеченных из этой строки, поделенных разделителями строк.
- String repeat(int): возвращает строку, значение которой представляет собой конкатенацию этой строки, повторяющееся количество раз.
- String strip(): Возвращает строку, значение которой является этой строкой, при этом удаляются все пробелы в начале и в конце строки.
- String stripLeading(): возвращает строку, значение которой является этой строкой, при этом удаляются все пробелы в начале строки.
- String stripTrailing(): возвращает строку, значение которой является этой строкой, при этом удаляются все пробелы в конце строки.
Скорее всего, вы посмотрите на strip() и спросите: «Как это отличается от существующего метода trim()?» Ответ заключается в разнице определения пробелов. (прим. переводчика: если коротко, strip() лучше понимает юникод, подробный разбор на StackOverflow)
java.lang.StringBuffer
java.lang.StringBuilder
Оба этих класса имеют новый метод compareTo(), который принимает StringBuffer/StringBuilder и возвращает int. Метод лексического сравнения аналогичен новому методу compareTo() в CharSequence.
java.lang.Thread
Никаких новых методов. Методы destroy() и stop(Throwable) были удалены. Метод stop(), который не принимает аргументов, все еще присутствует. Может привести к проблеме совместимости.
java.nio.ByteBuffer
java.nio.CharBuffer
java.nio.DoubleBuffer
java.nio.FloatBuffer
java.nio.LongBuffer
java.nio.ShortBuffer
Все эти классы теперь имеют метод mismatch(), который находит и возвращает относительный индекс первого несоответствия между этим буфером и переданным буфером.
java.nio.channels.SelectionKey
- int interestOpsAnd(int): Атомарно задает интерес этого ключа (key's interest) к побитовому пересечению («и») существующего набора интересов и переданного значения.
- int interestOpsOr(int): Атомарно задает интерес этого ключа (key's interest) к побитовому объединению («или») существующего набора интересов и переданного значения.
java.nio.channels.Selector
- int select(java.util.function.Consumer, long): выбор и выполнение действий над ключами, чьи соответствующие каналы готовы для операций ввода-вывода. long аргумент — таймаут.
- int select(java.util.function.Consumer): то же, что и выше, только без таймаута.
- int selectNow(java.util.function.Consumer): то же, что и выше, только неблокирующий.
java.nio.file.Files
- String readString(Path): считывает весь контент из файла в строку, декодируя из байт в символы с использованием кодировки UTF-8.
- String readString(Path, Charset): как указано выше, с разницей что декодирование из байт в символы происходит с использованием указанной Charset.
- Path writeString(Path, CharSequence, java.nio.file.OpenOption []): Запись CharSequence в файл. Символы кодируются в байты, используя кодировку UTF-8.
- Path writeString(Path, CharSequence, java.nio.file.Charset, OpenOption []): то же, что и выше, символы кодируются в байты, используя кодировку указанную в Charset.
java.nio.file.Path
- Path of(String, String []): возвращает Path из строкового аргумента пути или последовательности строк, которые при объединении образуют строку пути.
- Path of(net.URI): возвращает Path из URI.
java.util.Collection
- Object[] toArray(java.util.function.IntFunction): возвращает массив, содержащий все элементы в этой коллекции, используя предоставленную функцию генерации для аллоцирования возвращаемого массива.
java.util.concurrent.PriorityBlockingQueue
java.util.PriorityQueue
- void forEach(java.util.function.Consumer): Выполняет передаваемое действие для каждого элемента Iterable до тех пор, пока все элементы не будут обработаны, или действие не выбросит исключение.
- boolean removeAll(java.util.Collection): удаляет все элементы этой коллекции, которые также содержатся в указанной коллекции (дополнительная операция).
- boolean removeIf(java.util.function.Predicate): Удаляет все элементы из этой коллекции, которые удовлетворяют заданному предикату.
- boolean retainAll(java.util.Collection): Сохраняет только те элементы в этой коллекции, которые содержатся в передаваемой коллекции (дополнительная операция).
java.util.concurrent.TimeUnit
- long convert (java.time.Duration): конвертирует передаваемую Duration в этот тип.
java.util.function.Predicate
- Predicate not(Predicate): Возвращает предикат, который является отрицанием передаваемого предиката.
Это один из моих любимых новых API в JDK 11. В качестве примера вы можете преобразовать этот код:
lines.stream() .filter(s -> !s.isBlank())
lines.stream() .filter(Predicate.not(String::isBlank))
или если мы используем статический импорт:
lines.stream() .filter(not(String::isBlank))
Лично я считаю, что эта версия более понятна и лаконична.
java.util.Optional
java.util.OptionalInt
java.util.OptionalDouble
java.util.OptionalLong
- boolean isEmpty(): Если значение отсутствует, возвращает true, в противном случае — false.
java.util.regex.Pattern
- Predicate asMatchPredicate(): Я думаю, что это может быть жемчужина нового API JDK 11. Создает предикат, который проверяет, соответствует ли этот шаблон заданной строке ввода.
java.util.zip.Deflater
- int deflate(ByteBuffer): сжимает входные данные и заполняет ими указанный буфер.
- int deflate(ByteBuffer, int): сжимает входные данные и заполняет ими указанный буфер. Возвращает фактическое количество сжатых данных.
- void setDictionary(ByteBuffer): Устанавливает заданный словарь для сжатия в байты в данном буфере. Это перегруженная форма существующего метода, который теперь может принимать ByteBuffer, а не байтовый массив.
- void setInput(ByteBuffer): Устанавливает входные данные для сжатия. Также перегруженная форма существующего метода.
java.util.zip.Inflater
- int inflate(ByteBuffer): распаковывает байты в указанный буфер. Возвращает фактическое количество разархивированных байт.
- void setDictionary(ByteBuffer): Устанавливает заданный словарь в байты в данном буфере. Перегруженная форма существующего метода.
- void setInput(ByteBuffer): Устанавливает входные данные для декомпрессии. Перегруженная форма существующего метода.
javax.print.attribute.standard.DialogOwner
Это новый класс в JDK 11. Используется для поддержки запроса на диалог печати или настройки страницы. Должен отображаться поверх всех окон или определенного окна.
javax.swing.DefaultComboBoxModel
javax.swing.DefaultListModel
- void addAll(Collection): добавляет все элементы, присутствующие в коллекции.
- void addAll(int, Collection): добавляет все элементы, присутствующие в коллекции, начиная с указанного индекса.
javax.swing.ListSelectionModel
- int [] getSelectedIndices(): Возвращает массив всех выбранных индексов в выбранной модели, в порядке возрастания.
- int getSelectedItemsCount(): Возвращает количество выбранных элементов.
jdk.jshell.EvalException
- jshell.JShellException getCause(): возвращает обёртку throwable cause в исполняющем клиенте, представленным EvalException, или null, если причина не существует или неизвестна.
Новые фичи (не публичный API)
Java (и другие языки) поддерживает вложенные классы через внутренние классы. Для правильной работы компилятор должен выполнять некоторые трюки. Например:
public class Outer < private int outerInt; class Inner < public void printOuterInt() < System.out.println("Outer int java">public class Outer < private int outerInt; public int access$000() < return outerInt; >>
Этот JEP представляет концепцию "гнезда", где два члена одного гнезда (Outer и Inner из нашего примера) являются соседями. Два новых атрибута были добавлены в формата *.class файла: NestHost и NestMembers. Эти изменения также полезны для других, компилируемых в байткод, языков, поддерживающих вложенные классы.
Эта фича предоставляет три новых метода для java.lang.Class:
Эта функция также потребовала изменений в спецификации виртуальной машины Java (JVMS), в частности в разделе 5.4.4 «Access Control».
Этот JEP описывает расширение формата *.class файла для поддержки новой формы с константным пулом CONSTANT_Dynamic (часто упоминаемой в презентациях как condy). Идея динамической константы кажется оксюмороном, но, по сути, вы можете думать о ней как о финальном (final) значении в Java. Значение константного пула не задаётся на этапе компиляции (в отличие от других констант), а используется метод начальной загрузки (bootstrap method) для определения значения во время выполнения. Поэтому значение является динамическим, но, поскольку его значение задано только один раз, оно также является константным.
Эта функция в первую очередь будет полезна тем, кто разрабатывает новые языки и компиляторы. Кто будет генерировать байт-код и *.class файлы в для запуска на JVM. Это упростит некоторые задачи.
Эта фича предоставляет новый класс java.lang.invoke.ConstantBootstraps с девятью новыми методами. Я не буду перечислять их всех здесь; это методы начальной загрузки динамически вычисляемых констант.
Эта фича требовала внесения изменений в JVMS, в частности, в том, как используется специальный байтовый код invoke и раздел 4.4 «The Constant Pool».
Это был JEP, внесенный Red Hat. JVM теперь может использовать больше специализированных инструкций, доступных в наборе команд Arm 64. В частности, это улучшает работу методов sin(), cos() и log() класса java.lang.Math.
Red Hat также внесла свой вклад в этот JEP. Сборщик мусора Epsilon несколько необычен, поскольку он не собирает мусор! Он будет выделять новую память, если это потребуется при создании новых объектов, но не освобождает пространство, занятое объектами без ссылок.
Казалось бы, в чем тогда смысл? Есть, как минимум, два применения:
- Прежде всего, этот сборщик предназначен для того, чтобы новые алгоритмы GC были оценены с точки зрения их воздействия на производительность. Идея состоит в том, чтобы запустить пример приложения с Epsilon GC и сгенерировать метрику. Включается новый алгоритм GC, запускаются те же тесты и сравниваются результаты.
- Для очень коротких или маложивущих задач (подумайте о serverless функции в облаке), где вы можете гарантировать, что вы не превысите память, выделенную heap пространству. Это может повысить производительность за счет отсутствия накладных расходов (включая сбор статистики, необходимой для принятия решения о запуске сборщика) в коде приложения.
Если пространство кучи исчерпано, последующая работа JVM может быть сконфигурирована одним из трех способов:
- Вызывается обычный OutOfMemoryError.
- Выполнить сброс кучи
- Жёсткая остановка JVM и, возможно, выполнение внешней задачи (например, запуск отладчика).
Криптографические стандарты постоянно меняются и совершенствуются. В этом случае существующая схема Диффи-Хеллмана с эллиптической кривой заменяется на Curve25519 и Curve448. Это ключевая схема соглашения, определённая в RFC-7748.
Платформа Java поддерживает Unicode, для обеспечения обработки всех наборов символов. Поскольку Unicode был обновлен до версии 10, JDK также был обновлен для поддержки этой версии стандарта.
Я всегда заинтригован, чтобы увидеть, что разработчики Unicode включают в новые версии. Unicode 10 имеет 8 518 новых символов. Это включает в себя символ биткоиа, набор символов Nüshu (используемый китайскими женщинами для написания стихов), а также Soyombo и Zanabazar Square (являются символами, используемыми в исторических буддийских текстах для написания санскрита, тибетского и монгольского языков). Добавилось также много других Эмоджи, включая долгожданный (по-видимому) Colbert Emoji.
Не забывайте, что начиная с JDK 9 вы можете использовать UTF-8 в файлах свойств (.properties). Это означает, что любой символ Юникода может использоваться в таких файлах. Включая Emojis. Или Nüshu.
Flight Recorder — это низкоуровневая структура сбора данных для JVM. До JDK 11 это была коммерческая функция в готовом наборе Oracle JDK. Теперь, когда Oracle устраняет функциональные различия между Oracle JDK и OpenJDK, эта функция была внесена в OpenJDK.
JEP выделяет четыре основных свойства:
- Предоставление API для записи и чтения данных как событий
- Предоставить буферный механизм и формат двоичных данных
- Разрешить настройку и фильтрацию событий
- Предоставлять события для ОС, JVM HotSpot и библиотек JDK
Для этого есть два новых модуля: jdk.jfr и jdk.management.jfr.
Подобно JEP 324, это обновление шифров, используемых JDK. Реализация шифров ChaCha20 и ChaCha20-Poly1305, как указано в RFC 7539. ChaCha20 — это относительно новый потоковый шифр, который может заменить старый, небезопасный шифр потока RC4.
Немного удивительно, что это JEP, был внесён Google. Это дает возможность получить информацию о распределении кучи объектов Java в JVM.
- Достаточно низкая рабочая нагрузка, которая будет включена по умолчанию непрерывно
- Доступен через четко определенный программный интерфейс
- Может отображать все распределения
- Может быть определен независимым от реализации способом (то есть, не ограничиваясь конкретным алгоритмом GC или реализацией VM)
- Может предоставлять информацию о живых и мертвых объектах Java.
TLS 1.3 (RFC 8446) является "капитальным ремонтом" протокола TLS и обеспечивает значительное повышение безопасности и производительности по сравнению с предыдущими версиями. JDK теперь поддерживает это, хотя это не распространяется на Datagram Transport Layer Security (DTLS).
Это новый экспериментальный сборщик мусора, предназначенный для использования с приложениями, для которых требуется большая (многогигабайтная) куча и низкая задержка. Он использует кучу одного поколения (что немного необычно, учитывая общепринятый шаблон использования Weak Generational Hypothesis) и выполняет большинство (но не все) работы GC одновременно с приложением. Это делается используя механизм "барьера" при чтении, который перехватывает каждое чтение объекта из приложения и гарантирует правильность вернувшейся ссылки. Это устраняет проблему одновременного перемещения объектов во время работы потоков приложений.
ZGC — region-based (как и G1), NUMA aware и compacting сборщик мусора. Не предназначен как сборщик общего назначения.
Если вы действительно хотите pauseless сборщик мусора с низкой задержкой, от всего сердца могу порекомендовать C4 в нашей Zing JVM.
Nashorn был представлен в JDK 8 как более эффективная замена Rhino Javascript движку. Цель состоит в том, чтобы удалить Nashorn вместе с API и jjs инструментом из будущей версии Java. Когда это произойдет, пока не решено. Была предложена возможность использования Graal VM в качестве замены, но как это будет работать, пока неясно.
Pack200 — это схема сжатия для JAR-файлов, используется ещё со времён Java SE 5.0. С введением JPMS в JDK 9 Pack200 больше не используется для сжатия самого JDK. Инструменты pack200 и unpack200 и API Pack200 в java.util.jar теперь устарели и могут быть удалены в будущей версии JDK. Когда это произойдет, не указано.
Выводы
JDK 11 — следующая версия LTS JDK (так определяет Оракл за которым следуют все остальные). Несмотря на то, что функций, ориентированных на разработчиков, не так много, в JVM много что изменилось, это закладывает основу для будущих более важных функций.
Zulu сборки JDK 11 можно найти здесь и абсолютно бесплатно!
Пришло ли время начать перенос ваших приложений в JDK 11?
(прим. переводчика: просьба, все ошибки перевода и другие неточности присылать в ЛС)
