Всем привет! ???? Мы — Java-разработчики Т-Банка: Андрей, Арсений, Роман и Константин. Собираем интересные новости, статьи, туториалы и другие материалы из мира Java-разработки и делимся этим со всем сообществом.

В этом выпуске рассказываем о том, что Java 27 вышла с компактными заголовками объектов и G1 по умолчанию, Сбер представил собственный SberJDK, а Spring меняет подход к релизам из-за потока AI-найденных уязвимостей. Разбираем новые фичи, смотрим, как Netflix применяет AOT Cache, и выясняем, почему Spring Data JDBC — это не просто упрощенный Hibernate. Обо всем этом читайте в выпуске ?

Вышла Java 27

15 сентября состоялся релиз Java 27. Версия не является LTS, но содержит сразу несколько изменений, которые могут быть заметны даже приложениям, где не используются новые API.

Основные изменения:

  1. Compact Object Headers теперь включены по умолчанию. На 64-битных JVM заголовок обычного объекта уменьшается с 12 до 8 байт. Эффект особенно заметен в приложениях, где много небольших объектов: меньше heap, выше плотность данных в кэше процессора и меньше работы для GC.

  2. G1 теперь используется по умолчанию во всех окружениях. Раньше JVM могла автоматически переключаться на Serial GC, например, на небольших машинах или в контейнерах с ограниченными ресурсами. Поэтому после перехода на Java 27 стоит отдельно проверить потребление памяти и CPU небольшими инстансами.

  3. JFR получил встроенное редактирование чувствительных данных: значения аргументов командной строки, environment variables и system properties теперь могут быть скрыты еще внутри процесса, до попадания в запись.

  4. В релиз вошли постквантовый hybrid key exchange для TLS 1.3, очередные preview Structured Concurrency, Lazy Constants и primitive types в pattern matching, а Vector API дошел уже до двенадцатой incubator-итерации.

Главные новости

Сбер представил SberJDK. SberJDK — промышленная платформа на базе OpenJDK и целевая альтернатива для всех зарубежных сборок JDK/JRE. Базовая версия решения содержится в Едином реестре отечественного ПО. Исходный код написан на Java и C++ и опубликован GitVerse под лицензией GNU General Public License (GPL) без лицензионной платы за использование, в том числе в корпоративных системах. 

Представители Сбера утверждают, что перевели на SberJDK Java-ландшафт банка и подтвердили надежность базовой версии на критически важных для бизнеса системах. Упоминается, что внедрили ряд оптимизаций Java-платформы, разработанных с помощью ИИ. Указано, что реализованные решения обеспечивают прирост производительности порядка 20% на целевых нагрузках без изменения кода приложений. 

Spring AI 2.1.0-M1. Вышел первый milestone Spring AI 2.1. Одно из главных изменений — поддержка OpenAI Responses API. Появился новый структурированный ordered model для содержимого сообщений и возможность записывать заранее рассчитанные embeddings непосредственно в Vector Store. 

Отдельного внимания заслуживают изменения вокруг MCP: документация теперь подробнее описывает scope клиента и границы сессий, включая сценарии, когда для разных пользователей требуется изолированный MCP-клиент. Spring AI 2.1 также переезжает на baseline Spring Boot 4.2. Пока это milestone-релиз и API еще может измениться, но уже видно направление развития Spring AI от набора интеграций с LLM к полноценной инфраструктуре для AI-приложений.

Интересное видео

Principles of Memory Management in Java
Рон Пресслер разбирает, как на самом деле устроено управление памятью в Java и почему большой heap не обязательно означает неэффективное использование ресурсов. Он показывает, как JVM может использовать запас свободной памяти, чтобы реже запускать сборку мусора и экономить процессорное время, а уменьшение потребления памяти, наоборот, иногда приводит к дополнительным затратам CPU.

Полезные статьи

Новые фичи Java нужны не только на собеседованиях. Михаил Поливаха рассматривает две фичи современной Java: ScopedValue и sealed-типы. 

Автор приводит два кейса:
  • ScopedValue используется для хранения security-контекста с авторизационным токеном. Это нужно для реализации сквозной авторизации: токен берется из входящего запроса и используется в исходящем. Отмечается, что для этой задачи может подойти ThreadLocal, но есть ряд недостатков: мутационность переменной и необходимость явно ее очищать после использования. API ScopedValue этих проблем лишено.

  • Sealed-типы используются для контроля возможных реализаций конкретного интерфейса. Объекты, реализующие этот интерфейс, используются в качестве ключа Map, поэтому они должны быть immutable. Решение: пометить интерфейс sealed и указать единственную реализацию. При этом enums также могут подойти для этого, но такое решение будет менее гибким. Хотя остается непонятным, почему просто не отказаться от интерфейса и оставить единственную record-реализацию.

Оба примера отражают основную мысль: новые фичи не дают принципиально новых возможностей, но позволяют сократить вероятность ошибочного поведения программы на уровне реального кода, без использования комментариев и javadocs.

Трагедия версионирования ПО. Очень обстоятельная статья об одной из самых важных тем в разработке ПО — версионировании. Автор приводит несколько стратегий версионирования, обозначает сильные и слабые стороны каждой из них. А что самое полезное, дает конкретные рекомендации, какую стратегию использовать в зависимости от типа продукта. 

Краткая шпаргалка по версионированию

Consumer Application:

  • CalVer подойдет, если есть четкий график мажорных релизов. Пример: IDE от JetBrains.

  • Marketing Versioning подойдет, если мажорные релизы случаются редко. Пример: ОС Windows.

Библиотека:

  • SemVer подойдет на первое время, пока у проекта небольшое публичное API и мало зависимостей.

  • Pain Index. Более неформальная версия SemVer. Подходит, когда уже не получается строго следовать SemVer без необходимости делать каждый новый релиз мажорным. Изобретение команды Spring Boot. 

Несколько независимых компонентов, например APM-платформа вроде Dynatrace. 

В этом случае каждый конкретный компонент может использовать одну из стратегий выше. Вопрос в том, как синхронизировать совместимость версий нескольких компонентов:

  • Независимое версионирование и Compatibility Matrix. Просто для команд разработки, но пользователю приходится сверяться с мастером при обновлении. Пример: Spring Boot и Spring Cloud.

  • Lockstep versioning. При поднятии версии одного компонента создаются артефакты с такой же версией других компонентов, даже если в них не было изменений. Требует слаженности от команд разработки, но удобно для пользователей. Пример: Spring Boot.

  • Compatibility Window. Отдельно рассматривается вопрос обновления таких систем, если речь идет не просто о наборе стартеров, а о разных типах компонентов. Стратегия Compatibility Window подразумевает, что есть четкий порядок обновления компонентов. Соответственно, для каждого компонента указывается, с какими версиями связанных компонентов он совместим. Обновление идет с конца цепочки — каждый компонент обновляется до максимально допустимой версии согласно Compatibility Window. Пример: K8S.

Скрытый секрет конфигурации Spring Boot. EnvironmentPostProcessor.

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

В пример приводится задание специальных actuator-ендпоинтов. Автор сначала показывает несколько неудачных подходов и объясняет минусы каждого из них. В качестве решения предлагает EnviromentPostProcessor. Такие постпроцессоры запускаются в самом начале жизненного цикла приложения и позволяют настроить гибкую стратегию по конфигурации конкретного сервиса. В конце есть краткая сводка, в каких случаях стоит пользоваться именно EnviromentPostProcessor, а когда использовать более простые подходы. 

Project Loom in IntelliJ IDEA: Virtual Threads, Scoped Values, and Structured Concurrency — The JetBrains Blog. JetBrains на практическом примере разбирает три ключевые части Project Loom: виртуальные потоки, Scoped Values и Structured Concurrency. Показывает, как с их помощью упростить код на CompletableFuture, избавиться от ручной отмены задач и безопаснее передавать контекст между потоками, а заодно — как IntelliJ IDEA помогает отлаживать такую конкурентность.

JDK 27 Compact Object Headers and G1 Default: Measured on Spring Boot 4.1.Практический разбор двух изменений в JDK 27: Compact Object Headers и перехода на G1 в качестве сборщика мусора по умолчанию. Для эксперимента автор использует Spring-Boot-приложение, которое загружает 2 млн объектов Product в ConcurrentHashMap, а затем сравнивает JDK 26 и 27 по размеру отдельных объектов, live set и работе GC. Отдельно он повторяет тесты на JDK 27 с отключенными Compact Object Headers и разными сборщиками мусора, чтобы отделить эффект новых заголовков объектов от остальных изменений JVM. 

Как итог теста, результаты показывают 25% экономии памяти на уровне отдельного объекта Product (с 32 до 24 байт) и ~15% на уровне всего live set при загрузке 2 млн идентичных объектов.

Под капотом Kafka: путь сообщения от send() до commit offset. Подробный разбор жизненного цикла сообщения в Kafka — от вызова producer.send() до обработки consumer и фиксации offset. Автор последовательно проходит внутренние механизмы клиента и брокера: batching, RecordAccumulator, Sender, выбор partition, acknowledgements, работу ISR и дальнейшее получение данных consumer. 

Ценность статьи — в связи внутреннего устройства Kafka с реальными проблемами эксплуатации. После понимания того, на каком участке находится сообщение и какие компоненты участвуют в его обработке, становится проще разбираться с растущим consumer lag, задержками producer, проблемами с подтверждениями и другими типичными инцидентами.

Spring Data JDBC: Укрощение Строптивого. Большой материал о Spring Data JDBC и о том, где проходит граница между ним и привычным Hibernate. Основной акцент сделан не столько на API фреймворка, сколько на проектировании агрегатов. 

Автор постепенно усложняет доменную модель и показывает, почему решения, которые нормально выглядят с точки зрения классов Java, могут плохо ложиться на модель Spring Data JDBC. Отдельно разбираются вопросы: 

  • когда Spring Data JDBC действительно проще Hibernate; 

  • какие сущности должны входить в Aggregate; 

  • почему ссылки между агрегатами стоит проектировать аккуратно; 

  • как требования к загрузке и сохранению данных влияют на дизайн модели. 

Материал особенно полезен тем, кто рассматривает Spring Data JDBC как «Hibernate, только попроще». Статья хорошо показывает, что это скорее другой подход к persistence, чем облегченная ORM.

Любопытный подкаст

AOT Caching - Netflix' Practice vs OpenJDK's Theory [I/O] Inside Java Podcast 70
В одном из прошлых дайджестов мы уже рассказывали о применении AOT Cache в Netflix. На этот раз Nicolai Parlog возвращается к теме в формате подкаста и обсуждает ее с Martin Chalupa из Netflix и John Rose из OpenJDK: как canary-инстансы и реальный трафик используются для формирования AOT-кэша, что JVM уже умеет сохранять между запусками и как этот механизм планируют развивать дальше.

Просто интересное

Releasing Spring for Modern Challenges. Интересная заметка команды Spring о том, как меняется процесс разработки фреймворка из-за роста количества security-reports.

По словам команды, с марта они получают в среднем около 80 security-reports от сообщества в месяц. Одной из причин роста количества найденных проблем стало распространение AI-инструментов для поиска уязвимостей. 

В результате Spring меняет release train: вместо растянутого примерно на две недели процесса обновления разных проектов регулярный patch-релиз экосистемы теперь должен проходить в один день. 

Любопытен здесь даже не сам новый календарь релизов, а изменение процесса разработки крупного Open-Source-проекта под воздействием AI: модели ускоряют не только написание кода, но и поиск проблем в уже существующем коде. А инфраструктуре проекта приходится подстраиваться под новую скорость.

How we saved 100 terabytes of memory by optimizing 1.1.1.1’s DNS cache | Cloudflare Blog. Cloudflare рассказывает, как несколько относительно простых оптимизаций DNS-кэша 1.1.1.1 позволили сократить размер одной записи более чем вдвое и освободить около 100 ТБ памяти. Код в статье написан на Rust, но многие из разобранных приемов не привязаны к конкретному языку: авторы уменьшают количество аллокаций, избавляются от дублирования данных и подбирают более компактное представление структур, заодно ускоряя работу кэша.

Спасибо за прочтение! Ждем вашей обратной связи в комментариях. Увидимся через месяц ?

Присылайте материалы, если встретили что-то интересное, — опубликуем в следующем выпуске!

Комментарии (0)