Русский
Русский
English
Статистика
Реклама

Openjdk

Поваренная книга Quarkus Cookbook, бесплатный Developer Sandbox for OpenShift и руководство CentOS Project

25.02.2021 12:15:55 | Автор: admin

Мы собрали для вас короткий дайджест полезных материалов, найденных нами в сети за последние две недели. Оставайтесь с нами станьте частью DevNation!

Начни новое:

Качай:

  • Бесплатный Developer Sandbox for OpenShift
    Это ваш личный OpenShift в облаке, всего за пару минут и без какой бы то ни было инсталляции. Среда создана специально для разработчиков и включает в себя Helm-диаграммы, builder-образы Red Hat, инструменты сборки s2i, также CodeReady Workspaces, облачную IDE с веб-интерфейсом.

Мероприятия:

Подробнее..

Представляем Quarkus на Red Hat OpenShift

21.01.2021 14:15:10 | Автор: admin

Red Hat очень серьезно относится к будущему Java, остающейся самым популярным языком разработки корпоративных приложений. Почти две трети всех программистов мира относят себя к умеренным или заядлым пользователям Java, которая вот уже 18 лет подряд входит в тройку лучших языков программирования в рейтинге TIOBE. Можно смело сказать, что несмотря на небывалый выбор и доступность новых языков программирования, Java остается де-факто стандартом разработки критически важных бизнес-приложений. Мы в Red Hat считаем своим долгом поддерживать Java-разработчиков и прокладывать для них новые пути для продолжения инноваций.

Среди множества технологий, призванных сохранить роль и место Java в облачных реалиях будущего, одной из наиболее перспективных является Quarkus, фреймворк для разработки Kubernetes-нативных Java-приложений. Quarkus изначально создавался как способ оптимизировать Java для контейнерных сред, и, как результат, способен значительно повысить продуктивность разработчиков и снизить эксплуатационные расходы, превращаясь в ключевой компонент контейнерных рабочих нагрузок. Несмотря на свою молодость и бурный рост, Quarkus уже стал заметным игроком на рынке технологий разработки и задает новый формат работы программистов. Сегодня мы рады сообщить, что Quarkus теперь входит в состав Red Hat OpenShift, и это важный шаг для будущего Java как инструмента разработки современных облачных приложений.

Quarkus на Red Hat OpenShift

Quarkus и до этого уже полностью поддерживался и был доступен в рамках Red Hat Runtimes, но теперь он входит в состав и полностью интегрирован с Red Hat OpenShift, что еще больше упрощает разработку. Разработчики получают знакомые инструменты, возможность удаленной разработки на кластерах с помощью IDE, таких как CodeReady Workspaces, интеграцию с управляемыми конфигурациями, развертывание serverless-нагрузок и управление хранилищами приложений.

В состав Quarkus входят несколько компонентов для разработки и развертывания на OpenShift:

  • Quarkus-расширение генерация кода для новых проектов, управление зависимостями проектов, удаленная разработка и отладка, а также простое, в один шаг, развертывание в OpenShiftPlugins для CodeReady Workspaces, включая предварительно настроенные стеки рабочих пространств разработчика, подсказки по свойствам конфигураций и автозавершение кода, снипеты для стандартных типов классов Quarkus, а также создание и развертывание кода в OpenShift непосредственно из IDE.

  • Автоматическая привязка тестов исправности (health checks), подключение секретов и предоставление метрик для простой интеграции со средствами мониторинга, наподобие Prometheus

  • Автоматическое развертывание контейнеризированных приложений Quarkus в качестве serverless-нагрузок OpenShift.

  • Knative-развертывания в один шаг.

Также обеспечивается интеграция с Kubernetes API, включая generic API client и поддержку динамической конфигурации приложений с использованием Kubernetes ConfigMaps и Secrets.

Migration Toolkit for Applications

Мы также обновили Migration Toolkit for Applications, чтобы помочь с переносом приложения Spring Boot в Quarkus на OpenShift. Этот тулкит для консультантов, архитекторов и разработчиков помогает проанализировать Java-код или двоичные файлы с точки зрения различных вариантов трансформации (задаются в виде наборов правил) для эффективной модернизации и миграции приложений. Свежая версия Migration Toolkit for Applications сочетает правила Containerization, OpenJDK и Linux со специально разработанными и протестированными правилами для переноса кода Spring Boot в Quarkus на OpenShift, чтобы лучше учесть особенности гибридного облака.

Теперь Quarkus доступен еще большему число разработчиков, а чтобы узнать о нем больше и начать использовать, обратитесь к сайту: https://developers.redhat.com/products/quarkus/getting-started

Подробнее..

Как мы переходили на Java 15, или история одного бага в jvm длинной в 6 лет

12.02.2021 10:14:08 | Автор: admin

Мы готовились к выходу Java 15 ради некоторых её новых возможностей. В частности текстовых блоков. Да, они появились в Java 14 (о новых функциях в Java 14 можно посмотреть здесь), но только как превью-фича, а, начиная с Java 15, она стала доступна в виде окончательно готовой функции.

Мы в hh.ru привыкли внедрять и использовать самые современные технологии в разработке ПО. Пробовать что-то новое одна из ключевых задач команды Архитектура. Пока многие пишут на Java 8, мы уже близки к тому, чтобы отправить на свалку истории Java 11.

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

Переезд с Java 14 на Java 15. Что-то пошло не так

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

hh и в продакшн сервис обновлён, работа выполнена. Что дальше? А дальше мониторинг работы. Для сбора метрик мы используем okmeter. С его помощью мы наблюдали за поведением обновленного сервиса. Никаких аномалий по сравнению с предыдущей версией Java не было, кроме одной нативная память. В частности, зона Code Cache выросла почти в 2 раза!

До конца 17 ноября Java 14, после Java 15До конца 17 ноября Java 14, после Java 15

Для сервиса с большим количеством инстансов каждый мегабайт памяти на счету, и, помимо её резкого роста, на графике просматривается возрастающий тренд. Кажется мы имеем дело с утечкой нативной памяти в Code Cache.

Что такое вообще этот ваш Code Cache?

Code Cache область нативной памяти, где хранится интерпретатор байткода Java, JIT-компиляторы C1 и C2, и оптимизированный ими код. Основным пользователем является JIT. Весь перекомпилированный им код будет сохранятся в Code Cache.

Начиная с Java 9 Code Cache поделен на три отдельных сегмента, в каждом из которых хранится свой тип оптимизированного кода (JEP 197). Но на графике выше видно только одну выделенную область, несмотря на то что там Java 14 и Java 15. Почему одну?

Дело в том, что мы тонко настраивали размеры памяти при переводе сервисов в Docker (о том, как это было, можно почитать тут) и умышленно установили флаг размера Code Cache (ReservedCodeCacheSize) равным 72МБ в этом сервисе.

Три сегмента можно получить двумя путями: оставить значение ReservedCodeCacheSize по умолчанию (256Мб) или использовать ключ SegmentedCodeCache. Вот как эти зоны выглядят на графике с другого нашего сервиса:

Поиск утечки нативной памяти в Code Cache

С чего начать расследование? Первое что приходит на ум использовать Native Memory Tracking, функцию виртуальной машины HotSpot, позволяющую отслеживать изменение нативной памяти по конкретным зонам. В нашем случае использовать Native Memory Tracking нет необходимости, так как благодаря собранным метрикам, мы уже выяснили, что проблема в Code Cache. Поэтому мы решаем сделать следующее запустить инстансы сервиса с Java 14 и Java 15 вместе. Так как у нас уже три дня сервис работает на "пятнашке", добавляем один инстанс на 14-ой.

Мы решаем продолжить поиск утечки с помощью утилит Java. Начнем с jcmd. Так как мы знаем, что "течет" у нас Code Cache, мы обращаемся к нему. Если сервис запущен в Docker, можно выполнить команду таким образом для каждого инстанса:

docker exec <container_id> jcmd 1 Compiler.CodeHeap_Analytics

Получаем два очень длинных и подробных отчета о состоянии Code Cache. Кропотливо сравнив их, мы обратили внимание на следующий интересный факт, связанный с очисткой Code Cache:

// Java 14Code cache sweeper statistics:Total sweep time: 9999 msTotal number of full sweeps: 17833Total number of flushed methods: 10681 (thereof 1017 C2 methods)Total size of flushed methods: 20180 kB// Java 15Code cache sweeper statistics:Total sweep time: 5592 msTotal number of full sweeps: 236 Total number of flushed methods: 11925 (thereof 1146 C2 methods)Total size of flushed methods: 44598 kB

Обратите внимание на количество циклов полной очистки Total number of full sweeps. Вспомним, что сервис на Java 15 работает 3 дня, а на Java 14 всего 20 минут. Но количество полных очисток Code Cache поразительно разнится почти 18 тысяч за 20 минут, против 236 за трое суток.

Как работает очистка Code Cache

Пришло время углубиться в детали. За очистку Code Cache отвечает отдельный поток jvm CodeCacheSweeperThread, который вызывается с определенной эвристикой. Поток реализован как бесконечный цикл while, внутри которого он блокируется, пока не истечет 24-часовой таймаут, либо не будет снята блокировка вызовом:

CodeSweeper_lock->notify();

После того, как блокировка снята, поток проверяет, истек ли таймаут и имеет ли хотя бы один из двух флагов, запускающих очистку Code Cache, значение true. Только при выполнении этих условий, поток вызовет очистку Code Cache методом sweep(). Давайте подробнее разберем флаги:

should_sweep. Этот флаг отвечает за две стратегии очистки Code Cache нормальную и агрессивную. О стратегиях поговорим дальше.

force_sweep. Этот флаг устанавливается в true при необходимости принудительно очистить Code Cache без выполнения условий нормальной и агрессивной стратегий очистки. Используется в тестовых классах jdk.

Нормальная очистка

  1. Во время вызова GC хранящиеся в Code Cache методы могут изменить свое состояние по следующему сценарию: alive -> notentrant -> zombie. Методы не-alive помечаются как "должны быть удалены из Code Cache при следующем запуске потока очистки".

  2. В конце своей работы GC передает ссылку на все не-alive объекты в метод report_state_change.

  3. Далее в специальную переменную bytes_changed инкрементируется суммарный размер объектов, помеченных как не-alive в этом проходе GC.

  4. Когда bytes_changed достигает порога, задаваемого в переменной sweep_threshold_bytes, флаг should_sweep помечается как true и блокировка потока очистки снимается.

  5. Запускается алгоритм очистки Code Cache, в начале которого значение bytes_changed сбрасывается. Сам он состоит из двух фаз: сканирование стека на наличие активных методов, удаление из Code Cache неактивных. На этом нормальная очистка завершена.

Начиная с Java 15 пороговым значением можно управлять с помощью флага jvm SweeperThreshold он принимает значение в процентах от общего количества памяти Code Cache, заданном флагом ReservedCodeCacheSize.

Агрессивная очистка

Этот тип очистки появился еще в Java 9, как один из способов борьбы с переполнением Code Cache. Выполняется в тот момент, когда свободного места в памяти Code Cache становится меньше заранее установленного процента. Этот процент можно установить самостоятельно, используя ключ StartAggressiveSweepingAt, по умолчанию он равен 10.

В отличие от нормальной очистки, где мы ждем наполнения буфера "мертвыми" методами, проверка на старт агрессивной очистки выполняется при каждой попытке аллокации памяти в Code Cache. Другими словами, когда JIT-компилятор хочет положить новые оптимизированные методы в Code Cache, запускается проверка на необходимость запуска очистки перед аллокацией. Проверка эта довольно простая, если свободного места меньше, чем указано в StartAggressiveSweepingAt, очистка запускается принудительно. Алгоритм очистки такой же, как и при нормальной стратегии. И только после выполнения очистки, JIT сможет положить новые методы в Code Cache.

Что у нас?

В нашем случае размер Code Cache был ограничен 72 МБ, а флаг StartAggressiveSweepingAt мы не задавали, значит по умолчанию он равен 10. Если взглянуть на статистику очистки Code Cache, может показаться, что на Java 14 работает именно агрессивная стратегия. Дополнительно убедиться в этом нам помог тот же график, но с увеличенным масштабом:

Java 14Java 14

Он имеет зубчатую структуру, которая говорит о том, что очистка происходит часто, и, вероятно, методы по кругу выгружаются из Code Cache, в следующей итерации JIT-компиляции вновь попадают обратно, после снова удаляются etc.

Но как это возможно? Почему работает агрессивная стратегия очистки? По умолчанию она должна запускаться в тот момент, когда свободного места в Code Cache менее 10%, в нашем случаем только при достижении 65 мегабайт, но мы видим, что она происходит и при 30-35 мегабайтах занятой памяти.

Для сравнения, график с запущенной Java 15 выглядит иначе:

Java 15Java 15

Зубчатая структура отсутствует, есть плавный рост, затем очистка и снова рост. Разгадка где-то рядом.

Утечка не утечка

Так как работой Code Cache управляет jvm, мы отправились искать ответы в исходниках openJDK, сравнивая версии Java 14 и Java 15. В процессе поисков мы обнаружили интересный баг. Там сказано, что агрессивная очистка Code Cache работает неправильно с того момента, как ее внедрили в Java 9. Вместо старта агрессивной очистки при 10% свободного места, она вызывалась при 90% свободного места, то есть почти всегда. Другими словами, оставляя опцию StartAggressiveSweepingAt = 10, на деле мы оставляли StartAggressiveSweepingAt = 90. Баг был исправлен 3 июля 2020 года. А все дело было в одной строчке:

Этот фикс вошел во все версии Java после 9-ки. Но почему тогда его нет в нашей Java 14? Оказывается, наш docker-образ Java 14 был собран 15 апреля 2020 года, и тогда становится понятно, почему фикс туда не вошел:

Так значит и утечки нативной памяти в Code Cache нет? Просто всё время очистка работала неправильно, впустую потребляя ресурсы cpu. Понаблюдав еще несколько дней за сервисом на Java 15, мы сделали вывод, что так и есть. Общий график нативной памяти вышел на плато и перестал показывать тренд к росту:

скачок на графике - это переход на java 15скачок на графике - это переход на java 15

Выводы

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

  2. Разумное использование метрик помогает обнаружить потенциальные проблемы и аномалии

  3. Переходите на Java 15, оно того стоит. Вот тут список всех фич, которые появились в пятнашке

  4. Если вы используете Java 8, то у вас проблемы агрессивной очистки Code Cache нет, за отсутствием этого функционала как такового. Однако существует риск, что Code Cache может переполниться и JIT-компиляция будет принудительно отключена

Подробнее..

Вышла Java 16

16.03.2021 18:09:12 | Автор: admin

Вышла 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    }}
    
  • Метод Class.permittedSubclasses() переименован в Class.getPermittedSubclasses().


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 <= i; j++) {        consumer.accept(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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru