Чашечка Java
Open in Telegram
Лучшие материалы по Java на русском и английском Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels
Show more8 292
Subscribers
-224 hours
-67 days
-7230 days
Posts Archive
8 292
Две проверки при переходе на Spring Boot 4
В гайде Spring Boot 4.0 есть две отдельные проверки: свойства и тестовые зависимости.
spring-boot-properties-migrator подключается на время перехода. Он печатает диагностику переименованных и удалённых свойств при старте и временно переносит свойства во время выполнения. После исправления конфигурации зависимость нужно убрать.
Для тестов с @WithMockUser или @WithUserDetails одного spring-security-test теперь недостаточно. Гайд требует spring-boot-starter-security-test, чтобы аннотации работали корректно.
Этот стартер уже включает spring-boot-starter-test транзитивно. Временный мигратор убирают после перехода, а нужную тестовую инфраструктуру оставляют.8 292
JPMS-тесты: снаружи модуля или внутри
Зелёный тест в classpath ещё не проверяет границы Java-модуля: такой запуск игнорирует модульную информацию. В GradleX Java Module Testing можно настроить blackbox-, whitebox- и classpath-тесты.
Есть
module-info.java в наборе тестов — получается отдельный blackbox-модуль. При запуске его классы и ресурсы упаковываются в модульный JAR.
В whitebox-режиме тесты добавляются к основному модулю через --patch-module при компиляции и запуске. Так можно проверять внутренности, настраивая дополнительные requires, opensTo и exportsTo.
В README указаны версия плагина 1.8.1 и Gradle 7.4+. Для несовместимых с JPMS тестовых библиотек оставлен classpath-режим. Но проверку модульных границ он уже не даст.8 292
Что именно анализирует JLiSA
Статический анализ работает с моделью программы. В JLiSA Java-код разбирает Eclipse JDT, затем фронтенд строит графы потока управления для LiSA. Абстрактные домены, межпроцедурную стратегию и проверки можно выбирать независимо.
Например, интервальный домен описывает диапазоны значений. Проверяющий компонент использует результат анализа, чтобы искать нарушения свойств программы. Меняется домен — меняется точность модели.
Есть важная граница: поведение части стандартной библиотеки задано вручную через заглушки и семантику методов. Полного моделирования JDK здесь нет.
И не все режимы статические.
check анализирует программу, а validate и find-bug запускают её через JDI. Для этих команд нужны модули jdk.compiler и jdk.jdi.8 292
🎙 Открытое собеседование по Java с Senior-разработчиком из Uzum
Правильный ответ на собесе ещё не гарантирует оффер. На открытом мок-интервью разберём, что ещё оценивает интервьюер ↓
18 ноября в 19:00 МСК Сергей Чамкин, Senior-разработчик в Uzum, ex-Wildberries, проведёт техническое интервью с кандидатом!
У Сергея 7 лет коммерческого опыта с Java и Kotlin в highload-проектах. Он также проводил технические собеседования в EPAM, Wildberries и RNT Group.
За час эфира ты: → узнаешь, какие вопросы задают на технических собеседованиях по Java; → увидишь, какие ответы убеждают интервьюера, а какие вызывают сомнения; → поймёшь, какие темы стоит подтянуть перед собственным собеседованием.Кандидат увидит вопросы впервые. Никакого заранее подготовленного сценария ☝🏻 Участие бесплатное! Эфир проходит в рамках менторской программы ШОРТКАТ для разработчиков, которые хотят сменить работу, вырасти в грейде и зарплате или прокачать технические навыки. 🎁 Бонус за регистрацию: файл «50 эталонных ответов на сложные вопросы Java-интервью» Переходи в бота, чтобы зарегистрироваться на эфир и забрать подарок: @shortcut_sh_bot Реклама. О рекламодателе.
8 292
Какая JVM расходует ресурсы в Gradle-тестах
Когда тесты распараллелены, общего времени задачи мало для диагностики. InfoTestProcess связывает CPU, heap, GC и JIT каждого воркера с PID и задачей Gradle.
Агент записывает идентификаторы при старте JVM, а при завершении снимает метрики через MXBean. Плагин объединяет эти файлы по PID. Это итог по процессу, а не профиль отдельных методов. Отсутствующие снимки учитываются отдельно.
В версии 2.2.1 нужны Gradle 8.2+ и JDK 17+ у воркеров. Без Develocity отчёт остаётся в
build/info-test-process/statsTestTasks.json. В Build Scan при числе воркеров больше 950 сохраняются наиболее затратные по CPU, а усечение явно отмечается.8 292
Что считает circuit breaker, если рядом есть retry
Порядок обёрток определяет, какие вызовы попадают в окно breaker. Если retry снаружи, а breaker внутри, breaker видит каждую попытку. При обратном порядке он видит итог целого запроса после повторов. Три попытки могут стать тремя записями в окне или одной.
В учебном проекте Fuse разобраны оба варианта. В цепочке декораторов последняя добавленная политика оказывается внешней. От порядка обёрток зависит и момент, когда breaker узнает о сбое.
Отдельная ловушка — повторять
CallNotPermittedException от открытого breaker. До зависимости вызов уже не дошёл, а retry может продолжить тратить бюджет попыток и ждать backoff. Автор предлагает исключить этот тип из повторов.
Fuse полезен как разбор реализации. Для production его автор рекомендует Resilience4j.8 292
BTrace: временная трассировка работающей JVM
Когда нужен ответ на конкретный вопрос о работающем Java-приложении, BTrace позволяет подключить Java-пробу к процессу. Инструментация временная, без изменения кода приложения и его перезапуска.
В wiki BTrace для стабильной ветки 2.2.x указан namespace
org.openjdk.btrace. Это стоит учитывать при поиске примеров: старый скрипт и текущая версия инструмента могут использовать разные имена пакетов.
BTrace можно применять вместе с JFR: JFR даёт непрерывную историю, проба помогает собрать целевые наблюдения для текущей диагностики. Перед подключением нужно отдельно проверить совместимость с целевой JVM и права на attach. Описание инструмента не гарантирует, что проба будет безопасной, а накладные расходы — низкими на вашей нагрузке.8 292
Opaque token проверяется через сервер авторизации
При introspection Resource Server отправляет bearer token на endpoint сервера авторизации со своими учётными данными. В ответе Spring Security проверяет
active: true, а scopes превращает в authorities с префиксом SCOPE_.
По умолчанию principal — OAuth2AuthenticatedPrincipal. Имя аутентификации берётся из sub, если это поле есть в ответе. Для настройки нужны адрес introspection endpoint, client ID и client secret.
В стандартной цепочке проверка токена зависит от доступности introspection endpoint. Собственный кеш меняет частоту обращений к нему и поведение при отзыве токена. При такой настройке нельзя заранее обещать мгновенный отзыв.
Spring Security 7.1.2: Opaque Token
#spring8 292
Завершённый шаг Spring Batch при рестарте пропускается
Job упал на финальном шаге. При рестарте того же
JobInstance Spring Batch обычно пропускает шаги со статусом COMPLETED. Если проверку входных данных или уборку ресурсов нужно выполнять каждый раз, у шага задают allowStartIfComplete(true).
Другая настройка, startLimit, ограничивает число запусков шага. В примере документации лимит равен 2: после двух неудач на третьей попытке шаг уже не запускается. Повтор требует вмешательства, например изменения лимита или запуска нового JobInstance.
Настройки рестарта не отменяют уже отправленное письмо или выполненный внешний HTTP-запрос. Что произойдёт при повторе таких действий, нужно определить в самом шаге и его интеграциях.
Spring Batch 6.0.6: Configuring a Step for Restart
#spring8 292
G1 не использует остаток региона после большого объекта
G1 в JDK 25 считает humongous объект размером не меньше половины региона. Он попадает в последовательность соседних регионов old generation. Остаток последнего региона недоступен другим аллокациям до освобождения всего объекта.
Поэтому свободные байты в heap ещё не означают, что для следующего большого объекта найдётся достаточно соседних свободных регионов. Размер региона G1 выбирает автоматически, если его не задали через
-XX:G1HeapRegionSize.
Ставшие недостижимыми humongous-объекты обычно освобождаются на Remark или Full GC. Для массивов примитивов есть механизм раннего освобождения. Одна большая аллокация сама по себе не означает обязательный Full GC: поведение нужно сопоставлять с логами и заполнением heap.
Oracle: Humongous Objects, JDK 25
#jvm8 292
transferTo может скопировать меньше запрошенного
Вызов
FileChannel.transferTo(position, count, target) возвращает число реально переданных байтов. Оно может быть меньше count и даже равняться нулю. Например, у неблокирующего target может не хватить места в выходном буфере.
При повторном вызове нужно сдвинуть явный offset и уменьшить остаток на число переданных байтов. Позиция исходного FileChannel сама не меняется. Если offset уже достиг текущего размера файла, новых байтов не будет.
Если метод вернул ноль, повтор без ожидания готовности target или проверки конца файла может превратиться в пустой цикл.
ОС иногда передаёт данные прямо из файлового кеша. Это может ускорить передачу, но документация не обещает zero-copy для любой системы и любого канала.
FileChannel.transferTo, Java SE 25
#java8 292
Дерево процессов в ProcessHandle меняется во время обхода
Java-сервис запускает внешний конвертер, а тот порождает дочерние процессы.
children() даёт только прямых детей, descendants() — потомков всех уровней. Оба метода возвращают снимок, а не живую коллекцию.
Процесс из снимка может завершиться ещё до того, как до него дойдёт обход. Между проверкой isAlive() и действием тоже остаётся гонка. Хранить только PID тоже рискованно: после завершения процесса ОС может выдать этот номер другому.
Поэтому один обход descendants() не гарантирует, что вся группа процессов будет остановлена. Для управления внешней задачей нужны обработка ошибок и учёт ограничений ОС. ProcessHandle не даёт прав сверх тех, что есть у обычного нативного приложения.
ProcessHandle, Java SE 25
#java8 292
ClassValue хранит вычисленные данные для каждого класса
Таблицу методов для диспетчеризации или разобранные метаданные можно кешировать отдельно для каждого класса. Для такой задачи у JDK есть
ClassValue<T>: get(type) возвращает значение, связанное с конкретным Class, и при необходимости запускает computeValue(type).
При гонке вычисление может выполниться несколько раз. С классом свяжется одно значение, его и получат вызывающие потоки. Поэтому computeValue должен допускать повторный вызов. Если внутри отправлять платёж или регистрировать ресурс с необратимым побочным эффектом, гарантий «ровно один раз» не будет.
remove(type) убирает связь и инвалидирует устаревшие вычисления. При следующем обращении значение можно вычислить заново. Здесь удобно хранить именно данные, зависящие от класса, а не произвольный кеш по бизнес-ключам.
ClassValue, Java SE 25
#java8 292
🔴 Завтра открытое собеседование с Java-разработчиком
4 ноября (уже завтра!) в 19:00 МСК приходи онлайн и посмотри, как проходит настоящее техническое интервью на Middle Java-разработчика.
Как это будет:
📂 Виктор Анохин, Java/Kotlin-разработчик Wildberries, ex-Билайн, проведёт собеседование с разработчиком-добровольцем: будет задавать реальные вопросы и давать задачи; 📂 Виктор прокомментирует каждый ответ и объяснит, на что обращает внимание интервьюер; 📂 В конце можно будет задать Виктору любой вопрос.Виктор более 5 лет занимается enterprise-разработкой в финтехе, телекоме и e-commerce. Сейчас он ведущий разработчик банковских сервисов и системы лояльности в Wildberries. Ранее строил highload-микросервисы с нуля. Участие бесплатное. Эфир проходит в рамках менторской программы ШОРТКАТ для Java-разработчиков, которые хотят вырасти в грейде и зарплате и прокачать технические навыки. Переходи в нашего бота, чтобы получить ссылку на эфир → @shortcut_sh_bot Реклама. О рекламодателе.
8 292
StampedLock: читать поля можно до проверки, использовать — после
Оптимистическое чтение в
StampedLock обходится без удержания read lock. Поток получает stamp через tryOptimisticRead(), копирует поля в локальные переменные и вызывает validate(stamp). Использовать прочитанные значения можно после успешной проверки.
Если за это время другой поток захватывал write lock, проверка провалится. В примере Oracle чтение повторяют уже под read lock. До проверки поля могут противоречить друг другу. При обходе изменяемого массива или вызове метода по прочитанной ссылке могут понадобиться дополнительные проверки согласованности данных.
Это механизм для коротких участков с известным устройством данных. StampedLock не реентерабелен, а его политика не гарантирует предпочтения читателям или писателям.
StampedLock, Java SE 25
#java8 292
Kafka: heartbeat не заменяет вызовы poll
Consumer может отправлять heartbeat и при этом слишком долго обрабатывать пачку. В Java-клиенте Kafka 4.0 прогресс проверяется отдельно: интервал между вызовами
poll ограничивает max.poll.interval.ms.
Если интервал превышен, consumer рискует потерять партиции, а коммит может завершиться CommitFailedException. Размер пачки ограничивает max.poll.records. Увеличение интервала даёт больше времени на обработку, но может замедлить rebalance.
Если обработку вынести в worker, consumer должен продолжать вызывать poll. Auto commit отключают, партиции приостанавливают до конца обработки, а offsets коммитят вручную только после обработки. Сам KafkaConsumer не thread-safe: worker-потоки не должны произвольно вызывать его методы.
KafkaConsumer 4.0: Detecting Consumer Failures8 292
Micrometer: каждый новый 404 может раздувать метрику
Если в собственной инструментализации записывать сырой URI в тег метрики, запросы к
/missing/a, /missing/b и тысячам других несуществующих путей дадут тысячи разных значений тега.
Micrometer разбирает именно такой пример: если не ограничивать значения URI для 404, кардинальность метрики растёт с каждым новым отсутствующим ресурсом. Вместо произвольного пути можно использовать ограниченное значение вроде NOT_FOUND.
Это задача проектирования собственных тегов. Из примера не следует, что все интеграции Spring Boot по умолчанию записывают сырые 404-пути.
На схеме меняется только тег URI. Значения условные, измерений памяти здесь нет.
Micrometer: Tag Values8 292
NMT в HotSpot: сначала включить, потом искать рост
Native Memory Tracking нужно включить при старте JVM, например флагом
-XX:NativeMemoryTracking=summary. Если процесс уже работает без NMT, включить сбор через jcmd не получится.
После прогрева сохраните базу командой jcmd <pid> VM.native_memory baseline. Позже jcmd <pid> VM.native_memory summary.diff покажет изменения по категориям относительно неё.
NMT учитывает внутренние аллокации HotSpot, но не всю память стороннего native-кода. Рост категории даёт направление для диагностики, а не готовое доказательство утечки.
На карточке — команды из руководства Oracle Java 25.
Native Memory Tracking в HotSpot
#jvm #java8 292
ResourceLock в JUnit: параллельный доступ на чтение
В параллельных тестах JUnit Jupiter
@ResourceLock объявляет общий ресурс по строковому имени и режим доступа к нему.
Два теста с READ для одного ресурса могут выполняться одновременно. READ_WRITE конфликтует с любым другим доступом к тому же ресурсу. Для аннотации на тестовом методе блокировка захватывается до @BeforeEach и освобождается после @AfterEach.
Параллельный запуск нужно включить и задать режим выполнения, например через @Execution(CONCURRENT). Одного флага enabled для параллельного выполнения тестов недостаточно.
Это координация тестов Jupiter, объявивших один ресурс, а не блокировка между процессами ОС.
Parallel Execution и Synchronization8 292
Quarkus: три промаха кеша, один вызов метода
Несколько запросов одновременно обращаются к отсутствующему ключу. В Quarkus 3.27 с кешем на Caffeine аннотация
@CacheResult обрабатывает такие обращения вместе: первый вызов вычисляет значение, остальные ждут результат.
Это работает в пределах одного кеша, одного ключа и одного экземпляра приложения. У объектов, входящих в ключ, должны корректно работать equals() и hashCode(). Разные ключи обрабатываются отдельно. Между экземплярами приложения такой блокировки нет.
lockTimeout задаёт предел ожидания блокировки. Это не таймаут выполнения самого метода. По умолчанию предел ожидания отключён.
При попадании в кеш тело метода не выполняется.
Кеширование в Quarkus 3.27