Всем привет! Есть такое правило - в логах в ПРОДе не должно быть ничего лишнего. Т.е. с одной стороны логи нужны для хранения стек-трейса исключения и другой полезной при разборе ошибки информации. Есть много инструментов, позволяющих строить по логам метрики, их парсить по заранее настроенной маске, вытаскивая полезную информацию. А с другой стороны логирование, как и любой другой функционал, жрет ресурсы. Как много? Вот так. На картинке показано число обработанных запросов для простейшего контроллера с логированием и без. Причём логирование происходило в RAM диск, т.е не учитываем задержки файловой системы. И шаблон сообщения в лог простой, контекст логирования для тех фреймворков, которые позволяют его настраивать, не используется. Вывод: отказываться от логирования не стоит, асинхрон рулит, а главное про уровни логирования и их настройку на ПРОДе забывать не стоит. Подробнее про условия теста: https://blog.sebastian-daschner.com/entries/logging-performance-comparison #java #logging #performance
Всем привет. Немного про цену создания объектов в Java. Для короткоживущих объектов на последних версиях JVM выигрыш от переиспользования объектов про сравнению с созданием составляет пример 25%. Справедливости ради на Java 8 разница была в 40%, т.е garbage collection развивается. Описание эксперимента тут http://blog.vanillajava.blog/2022/09/java-is-very-fast-if-you-dont-create.html #java #performance
blog.vanillajava.blog
Java is Very Fast, If You Don’t Create Many Objects
You still have to watch how many objects you create. This article looks at a benchmark passing events over TCP/IP at 4 billion events per ...
С другой стороны Java = объекты. Сборщики мусора стали достаточно умными, чтобы запускаться только тогда, когда памяти не хватает, и достаточно быстро убирать короткоживущихе объекты. Можно выбрать сборщик мусора либо с минимальными паузами, либо с минимальным overhead по ресурсам. Про выбор можно почитать тут https://www.baeldung.com/java-choosing-gc-algorithm Перераспределение памяти по ходу работы программы и роста heap можно убрать установив одинаковые xms и xmx, тогда JRE заберёт эту память из системы «навсегда» при старте приложения. Если на сервере много памяти и вы уверены, что heap-а точно хватит на все время работы программы - есть фейковый GC, который имеет ровно одну фичу - падать когда память кончается) https://openjdk.org/jeps/318 Когда это все не работает - примерно на десятках миллионах RPS как в статье из примера выше) #java #gc #performance
Всем привет!
На каких принципах постороены современные высокопроизводительные системы?
Не претендую на полный список, но попробую собрать основные архитектурные принципы с примерами реализующих их систем.
1) shared nothing - каждый запрос на обновление пользовательских данных обрабатывается одним (!) экземпляром сервиса. Пропадает необходимость в распределенных транзакциях или использовании паттерна "Сага", и т.об. повышается скорость и надежность. Технически это горизонтальное масштабирование сервиса\балансировщиков\проксей плюс шардирование хранилища и кэша Примеры: Kafka, Kafka Streams, Spark, Terradata, Hadoop, Solr, ElasticSearch... На примере Kafka: каждый брокер получает свою долю партиций - частей на которые делятся топики - и отвечает за чтение, запись из них, а также репликацию данных. Да, всему кластеру Kafka приходится шарить метаданные о расположении партиций на брокерах - в Zookepper в текущих версиях и в специальных топиках с метаданными в последней версии. И да, ответственный за патрицию может меняться. Но за запросы к пользовательским данным в партиции в каждый момент времени отвечает один брокер, на остальные брокеры эта информация только реплицируется. Репликация проходит асинхронно, без привязки к запросу клиента. Еще примеры: https://dimosr.github.io/shared-nothing-architectures/
2) data locality - данные хранятся на той же ноде, где проходят вычисления. Нет лишних сетевых запросов - быстрее обработка данных. Примеры: Kafka Streams, Spark, Hadoop. На примере Kafka Streams - любые методы, агрегирующие и трансформирующие данные стрима, работают только с данными из тех партиций Kafka, которые лежат на локальной машине. Только так получится добиться приемлемой производительности поточной обработки данных (streaming) в распределенной системе.
3) append-only или log-based storage - данные сохраняются добавлением записи в файл, никаких обновлений и удалений на уровне записей не происходит, файлы ротируются, устаревшие файлы удаляются целиком. Где-то рядом хранится указатель на текущую запись в файле. Т.к последовательная запись на порядок быстрее случайной, то append-only сильно ускоряет запись. Примеры: снова Kafka, Hadoop, Lucene, этот же принцип лежит в основе техник write-ahead logging (WAL) в журналах упреждающей записи СУБД и CQRS + Event Sourcing. Немного о последней: https://www.baeldung.com/cqrs-event-sourcing-java . И о том, как работает WAL https://habr.com/ru/company/postgrespro/blog/459250/ И о том, как Kafka сохраняет данные: https://mbukowicz.github.io/kafka/2020/05/31/how-kafka-stores-messages.html
4) zero-copy - в общем случае данные при чтении из диска и к примеру отправке по сети копируются в памяти несколько раз из буфера в буфер. Почему? Потому что буферы у файлового драйвера, у сетевого драйвера и у Java разные. Но этого можно избежать и работать с данными из буфера ОС, если они не меняются вашим сервисом или меняются, но используются одним процессом. Естественно это ускоряет работу с данными. zero copy должен поддерживаться на уровне ОС, Linux поддерживает. Примеры использования: опять Kafka. Как это работает в Kafka https://andriymz.github.io/kafka/kafka-disk-write-performance/ Про zero copy в Java я упоминал в https://t.me/javaKotlinDevOps/17, вот тут детальнее https://shawn-xu.medium.com/its-all-about-buffers-zero-copy-mmap-and-java-nio-50f2a1bfc05c
to be continued
P.S. Во всех 4 пунктах упоминается Kafka, и это не случайность)
#arch #Kafka #performance
На каких принципах постороены современные высокопроизводительные системы?
Не претендую на полный список, но попробую собрать основные архитектурные принципы с примерами реализующих их систем.
1) shared nothing - каждый запрос на обновление пользовательских данных обрабатывается одним (!) экземпляром сервиса. Пропадает необходимость в распределенных транзакциях или использовании паттерна "Сага", и т.об. повышается скорость и надежность. Технически это горизонтальное масштабирование сервиса\балансировщиков\проксей плюс шардирование хранилища и кэша Примеры: Kafka, Kafka Streams, Spark, Terradata, Hadoop, Solr, ElasticSearch... На примере Kafka: каждый брокер получает свою долю партиций - частей на которые делятся топики - и отвечает за чтение, запись из них, а также репликацию данных. Да, всему кластеру Kafka приходится шарить метаданные о расположении партиций на брокерах - в Zookepper в текущих версиях и в специальных топиках с метаданными в последней версии. И да, ответственный за патрицию может меняться. Но за запросы к пользовательским данным в партиции в каждый момент времени отвечает один брокер, на остальные брокеры эта информация только реплицируется. Репликация проходит асинхронно, без привязки к запросу клиента. Еще примеры: https://dimosr.github.io/shared-nothing-architectures/
2) data locality - данные хранятся на той же ноде, где проходят вычисления. Нет лишних сетевых запросов - быстрее обработка данных. Примеры: Kafka Streams, Spark, Hadoop. На примере Kafka Streams - любые методы, агрегирующие и трансформирующие данные стрима, работают только с данными из тех партиций Kafka, которые лежат на локальной машине. Только так получится добиться приемлемой производительности поточной обработки данных (streaming) в распределенной системе.
3) append-only или log-based storage - данные сохраняются добавлением записи в файл, никаких обновлений и удалений на уровне записей не происходит, файлы ротируются, устаревшие файлы удаляются целиком. Где-то рядом хранится указатель на текущую запись в файле. Т.к последовательная запись на порядок быстрее случайной, то append-only сильно ускоряет запись. Примеры: снова Kafka, Hadoop, Lucene, этот же принцип лежит в основе техник write-ahead logging (WAL) в журналах упреждающей записи СУБД и CQRS + Event Sourcing. Немного о последней: https://www.baeldung.com/cqrs-event-sourcing-java . И о том, как работает WAL https://habr.com/ru/company/postgrespro/blog/459250/ И о том, как Kafka сохраняет данные: https://mbukowicz.github.io/kafka/2020/05/31/how-kafka-stores-messages.html
4) zero-copy - в общем случае данные при чтении из диска и к примеру отправке по сети копируются в памяти несколько раз из буфера в буфер. Почему? Потому что буферы у файлового драйвера, у сетевого драйвера и у Java разные. Но этого можно избежать и работать с данными из буфера ОС, если они не меняются вашим сервисом или меняются, но используются одним процессом. Естественно это ускоряет работу с данными. zero copy должен поддерживаться на уровне ОС, Linux поддерживает. Примеры использования: опять Kafka. Как это работает в Kafka https://andriymz.github.io/kafka/kafka-disk-write-performance/ Про zero copy в Java я упоминал в https://t.me/javaKotlinDevOps/17, вот тут детальнее https://shawn-xu.medium.com/its-all-about-buffers-zero-copy-mmap-and-java-nio-50f2a1bfc05c
to be continued
P.S. Во всех 4 пунктах упоминается Kafka, и это не случайность)
#arch #Kafka #performance
A curious mind
Shared-nothing architectures
An overview of shared-nothing architectures, their pros and cons
10) горизонтальное масштабирование. Поддерживается Kafka (хотя она не является в чистом виде хранилищем), Cassandra, Riak и многими noSQL СУБД. Проблемы: переход с реляционной БД на noSQL не всегда возможен из-за структуры БД, отсутствия опыта работы с noSQL. Кроме того к проблемам несогласованного чтения добавляются проблемы несогласованной записи, они же конфликты записи. Тоже отдельная большая тема.
#storage #performance #jpa
#storage #performance #jpa
Всем привет!
Пару слов про gRPC - новый модный молодежный протокол для интеграции приложений.
Лично я всегда считал основных его преимуществом бинарный формат данных, а, следовательно, быстродействие.
Но это один из трех китов - трех преимуществ gRPC.
Какие же два других?
2) проблема общепринятого сейчас REST-а - в нем не было и нет встроенной схемы данных. Да, есть JSON Schema, OpenAPI и Consumer Driven Contracts. Но где-то они есть, а где-то - нет, причем это могут быть работающие вместе клиент и сервер) Можно же просто получить строку ответа и распарсить ее самостоятельно. И чем больше компания, чем больше у нее микросервисов внутри - тем сложнее будет поддержка и обновление зоопарка REST сервисов со временем. С этим столкнулись Google - разработчик gRPC, Netflix, Dropbox, Facebook - разработчик Thrift, аналога gRPC. В gRPC она есть, из нее генерируется код сервера и клиента. Не весь конечно, сервисная часть - без инфраструктурной и бизнес-логики. Schema first подход, без вариантов)
3) в схему gRPC изначально встроена возможность стриминга. Т.е. можно работать в режиме запрос-ответ, а можно использовать такие комбинации как:
а) запрос - несколько ответов
б) несколько запросов - один ответ
в) двунаправленный стриминг, где логика последовательности запросов и ответов определяется бизнес-процессом.
REST такое не умеет.
Причем схема со стримингом отличается от схемы запроса-ответа буквально одним словом. Код сервера\клиента конечно отличается сильнее)
Из минусов я бы отметил применимость в первую очередь для внутренних взаимодействий, наружу лучше выставлять REST или GraphQL, т.к. потребителям они понятнее. Также могут быть проблемы при изменениях, ломающих обратную совместимость, т.к. из-за бинарности и компактности формата данных жестко зафиксирован порядок полей в запросе\ответе. Возможно где-то будет проблемой то, что gRPC требует HTTP/2, в том же k8s\Openshift траффик HTTP и gRPC нужно разводить по разным портам. Ну и лично меня очень удивляет использование термина Stub в сгенерированном клиенте. Stub и в "боевом" коде... выглядит странно)))
#gRPC #integration #performance
Пару слов про gRPC - новый модный молодежный протокол для интеграции приложений.
Лично я всегда считал основных его преимуществом бинарный формат данных, а, следовательно, быстродействие.
Но это один из трех китов - трех преимуществ gRPC.
Какие же два других?
2) проблема общепринятого сейчас REST-а - в нем не было и нет встроенной схемы данных. Да, есть JSON Schema, OpenAPI и Consumer Driven Contracts. Но где-то они есть, а где-то - нет, причем это могут быть работающие вместе клиент и сервер) Можно же просто получить строку ответа и распарсить ее самостоятельно. И чем больше компания, чем больше у нее микросервисов внутри - тем сложнее будет поддержка и обновление зоопарка REST сервисов со временем. С этим столкнулись Google - разработчик gRPC, Netflix, Dropbox, Facebook - разработчик Thrift, аналога gRPC. В gRPC она есть, из нее генерируется код сервера и клиента. Не весь конечно, сервисная часть - без инфраструктурной и бизнес-логики. Schema first подход, без вариантов)
3) в схему gRPC изначально встроена возможность стриминга. Т.е. можно работать в режиме запрос-ответ, а можно использовать такие комбинации как:
а) запрос - несколько ответов
б) несколько запросов - один ответ
в) двунаправленный стриминг, где логика последовательности запросов и ответов определяется бизнес-процессом.
REST такое не умеет.
Причем схема со стримингом отличается от схемы запроса-ответа буквально одним словом. Код сервера\клиента конечно отличается сильнее)
Из минусов я бы отметил применимость в первую очередь для внутренних взаимодействий, наружу лучше выставлять REST или GraphQL, т.к. потребителям они понятнее. Также могут быть проблемы при изменениях, ломающих обратную совместимость, т.к. из-за бинарности и компактности формата данных жестко зафиксирован порядок полей в запросе\ответе. Возможно где-то будет проблемой то, что gRPC требует HTTP/2, в том же k8s\Openshift траффик HTTP и gRPC нужно разводить по разным портам. Ну и лично меня очень удивляет использование термина Stub в сгенерированном клиенте. Stub и в "боевом" коде... выглядит странно)))
#gRPC #integration #performance
Всем привет!
Сегодня пост с картинками, поэтому ловите https://telegra.ph/Urovni-izolyacii-sovremennogo-Java-prilozheniya-07-29
#java #jvm #docker #performance
Сегодня пост с картинками, поэтому ловите https://telegra.ph/Urovni-izolyacii-sovremennogo-Java-prilozheniya-07-29
#java #jvm #docker #performance
Telegraph
Уровни изоляции современного Java приложения
Всем привет! Уже писал про плюсы Docker - https://t.me/javaKotlinDevOps/165 Но все ли так безоблачно?) Обычно, когда сравнивают Docker с виртуальной машиной (VM) приводят такую схему Из нее видно, что Docker - более легковесное решение по сравнению с VM.…
Всем привет!
Нашёл отличное сравнение скорости конкатенации строк разными методами, от + до StringBuffer, StringBuilder и StringJoiner. И даже есть такая экзотика как String.format со стримами.
Для затравки три интересных факта.
1) StringBuffer даже с синхронизацией существенно быстрее обычной конкатенации.
2) String.format очень(!) медленный.
3) скорость обычной конкатенации с увеличением числа строк растёт экспоненциально.
Подробнее тут https://www.baeldung.com/java-string-concatenation-methods
#java #performance #string
Нашёл отличное сравнение скорости конкатенации строк разными методами, от + до StringBuffer, StringBuilder и StringJoiner. И даже есть такая экзотика как String.format со стримами.
Для затравки три интересных факта.
1) StringBuffer даже с синхронизацией существенно быстрее обычной конкатенации.
2) String.format очень(!) медленный.
3) скорость обычной конкатенации с увеличением числа строк растёт экспоненциально.
Подробнее тут https://www.baeldung.com/java-string-concatenation-methods
#java #performance #string
Baeldung
Performance Comparison Between Different Java String Concatenation Methods | Baeldung
Explore different string concatenation methods in Java and evaluate their performance using JMH.
Всем привет!
Сегодня расскажу про технологию native image.
Стандартная схема работы JVM приложения такая:
1) компилятор превращает исходники в байт-код
2) байт-код запускается на JVM
3) в процессе работы JVM анализирует использование байт-кода и при необходимости оптимизирует его, включая компиляцию в бинарное представление для конкретной процессорной архитектуры. И основные оптимизации надо отметить происходят именно здесь, а не при первичной компиляции. Еще важный момент - классы\библиотеки подгружаются в память не обязательно при старте приложения, а по мере использования. Все это называется JIT - Just in time компиляция. Влиять на нее можно с помощью ряда флагов запуска Java приложения - -server, -client.
Плюс такого подхода - JVM позволяет в 90% случаев игнорировать, на каком железе запускается Java приложение. Минус - долгий старт Java приложения плюс время для "разогрева" и выхода на рабочий режим.
Но с другой стороны с развитием Docker мы и так можем игнорировать особенности железа и ОС на хост-сервере, главное, чтобы там можно было запустить Docker. И наконец кроме долгого старта и разогрева собственно JVM у нас как правило есть Spring с кучей модулей, число которых растет, и в итоге время старта типичного Spring Boot приложения доходит до совсем неприличных величин.
Альтернатива - AOT - Ahead-of-Time compilation. В этом случае мы компилируем исходники в бинарный код в момент первичной компиляции. Причем как собственно приложение, так и JVM и все JAR. Получается такой native image монолит. Проект называется GraalVM https://www.graalvm.org/, официально поддерживается Oracle. Есть open-source версия, основанная на OpenJDK.
Плюс этого подхода - скорость запуска. Это критически важно в облаках, т.к. k8s может "случайно" рестартовать под при изменении конфигурации железа или настроек Deployment. Еще будет выигрыш в скорости обработки запросов, т.к. не тратится CPU и память в runtime на JIT компиляцию.
Какие минусы?
1) невозможна динамическая\ленивая загрузка библиотек\плагинов, classpath фиксируется в момент компиляции. К слову - у этого ограничения есть и плюсы, сложнее эксплуатировать уязвимости типа log4j injection - см. https://t.me/javaKotlinDevOps/4
2) вопрос - откуда компилятор узнает, какой код ему нужно добавить в наш native монолит? Ответ: он идет от метода main. Соответственно, код который явно не вызывается, а, например, вызывается через рефлексию, он не увидит. Соответственно, никакой рефлексии в ПРОМ коде. Что, надо сказать, в целом правильно)
3) аналогично просто так не заработает магия Spring, основанная на рефлексии и динамических прокси. Из чего следует, что мало добавить в Spring приложение AOT компилятор - нужно дорабатывать сам Spring, что и было сделано в Spring Boot 3.2. Другие фреймворки также придется дорабатывать. Например, Mockito до сих пор не работает в native image. Справедливости ради тут причина такая же, как в анекдоте про неуловимого ковбоя Джо - не нужен Mockito в native image)
4) если продолжить про Spring - загрузка бинов по условию: @ConditionalOnProperty, @Profile - тоже не заработает. Нужно указывать при сборке необходимый профиль, чтобы уже при компиляции нужные бины были обнаружены и добавлены в дистрибутив.
5) еще вопрос - но ведь среднее Java приложение + библиотеки + JVM = миллионы строк кода, что будет с компиляцией? Ответ - компиляция будет долгой, до 10 минут на spring boot hello world. Поэтому в документации Spring прямо сказано, что хотя Spring поддерживает запуск тестов в native image - делать так нужно только для интеграционных тестов, лучше на CI, а модульные запускать по старинке, т.к. тут критична скорость получения результата.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #java_start_boost
Сегодня расскажу про технологию native image.
Стандартная схема работы JVM приложения такая:
1) компилятор превращает исходники в байт-код
2) байт-код запускается на JVM
3) в процессе работы JVM анализирует использование байт-кода и при необходимости оптимизирует его, включая компиляцию в бинарное представление для конкретной процессорной архитектуры. И основные оптимизации надо отметить происходят именно здесь, а не при первичной компиляции. Еще важный момент - классы\библиотеки подгружаются в память не обязательно при старте приложения, а по мере использования. Все это называется JIT - Just in time компиляция. Влиять на нее можно с помощью ряда флагов запуска Java приложения - -server, -client.
Плюс такого подхода - JVM позволяет в 90% случаев игнорировать, на каком железе запускается Java приложение. Минус - долгий старт Java приложения плюс время для "разогрева" и выхода на рабочий режим.
Но с другой стороны с развитием Docker мы и так можем игнорировать особенности железа и ОС на хост-сервере, главное, чтобы там можно было запустить Docker. И наконец кроме долгого старта и разогрева собственно JVM у нас как правило есть Spring с кучей модулей, число которых растет, и в итоге время старта типичного Spring Boot приложения доходит до совсем неприличных величин.
Альтернатива - AOT - Ahead-of-Time compilation. В этом случае мы компилируем исходники в бинарный код в момент первичной компиляции. Причем как собственно приложение, так и JVM и все JAR. Получается такой native image монолит. Проект называется GraalVM https://www.graalvm.org/, официально поддерживается Oracle. Есть open-source версия, основанная на OpenJDK.
Плюс этого подхода - скорость запуска. Это критически важно в облаках, т.к. k8s может "случайно" рестартовать под при изменении конфигурации железа или настроек Deployment. Еще будет выигрыш в скорости обработки запросов, т.к. не тратится CPU и память в runtime на JIT компиляцию.
Какие минусы?
1) невозможна динамическая\ленивая загрузка библиотек\плагинов, classpath фиксируется в момент компиляции. К слову - у этого ограничения есть и плюсы, сложнее эксплуатировать уязвимости типа log4j injection - см. https://t.me/javaKotlinDevOps/4
2) вопрос - откуда компилятор узнает, какой код ему нужно добавить в наш native монолит? Ответ: он идет от метода main. Соответственно, код который явно не вызывается, а, например, вызывается через рефлексию, он не увидит. Соответственно, никакой рефлексии в ПРОМ коде. Что, надо сказать, в целом правильно)
3) аналогично просто так не заработает магия Spring, основанная на рефлексии и динамических прокси. Из чего следует, что мало добавить в Spring приложение AOT компилятор - нужно дорабатывать сам Spring, что и было сделано в Spring Boot 3.2. Другие фреймворки также придется дорабатывать. Например, Mockito до сих пор не работает в native image. Справедливости ради тут причина такая же, как в анекдоте про неуловимого ковбоя Джо - не нужен Mockito в native image)
4) если продолжить про Spring - загрузка бинов по условию: @ConditionalOnProperty, @Profile - тоже не заработает. Нужно указывать при сборке необходимый профиль, чтобы уже при компиляции нужные бины были обнаружены и добавлены в дистрибутив.
5) еще вопрос - но ведь среднее Java приложение + библиотеки + JVM = миллионы строк кода, что будет с компиляцией? Ответ - компиляция будет долгой, до 10 минут на spring boot hello world. Поэтому в документации Spring прямо сказано, что хотя Spring поддерживает запуск тестов в native image - делать так нужно только для интеграционных тестов, лучше на CI, а модульные запускать по старинке, т.к. тут критична скорость получения результата.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #java_start_boost
Есть еще ряд интересных моментов. Я расскажу про них на примере Spring Boot native image.
Для борьбы с тем, что часть кода недостижима если идти от точки входа (метод main), есть два инструмента.
1) специальный tracing агент, который можно подключить к приложению, и он будет в runtime логировать такие скрытые вызовы. https://www.graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/
2) далее можно создать т.наз. hints - подсказки AOT компилятору, что включить в native image, из того, что он не нашел сам - https://www.graalvm.org/latest/reference-manual/native-image/metadata/ Собственно, большая доля в адаптации фреймворка типа Spring для native image - подготовка таких hints, https://docs.spring.io/spring-boot/docs/3.2.1/reference/html/native-image.html
А что делать если в момент сборки еще не ясно - нужен native image или нет? Или нужны обе версии? Нет проблем - можно совместить оба режима JIT и AOT и создать артефакт, Spring Boot Executable Jar, с байткодом и всеми необходимыми для native image метаданными. И собрать из него native image позже в DevOps pipeline при необходимости.
Для Spring Boot есть два режима сборки. Основной - Native Image Using Buildpacks, в котором в итоге получается docker образ. Для него нужен только Docker на машине-сборщике. И т.наз. Native Build Tools - нужно устанавливать дистрибутив GraalVM, содержащий эти tools, в итоге получается бинарник для железа, на котором происходит сборка.
Итого - штука полезная, но только если вас категорически не устраивает время запуска приложения и все используемые вами фреймворки поддерживают native image.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #startup_time
Для борьбы с тем, что часть кода недостижима если идти от точки входа (метод main), есть два инструмента.
1) специальный tracing агент, который можно подключить к приложению, и он будет в runtime логировать такие скрытые вызовы. https://www.graalvm.org/22.3/reference-manual/native-image/metadata/AutomaticMetadataCollection/
2) далее можно создать т.наз. hints - подсказки AOT компилятору, что включить в native image, из того, что он не нашел сам - https://www.graalvm.org/latest/reference-manual/native-image/metadata/ Собственно, большая доля в адаптации фреймворка типа Spring для native image - подготовка таких hints, https://docs.spring.io/spring-boot/docs/3.2.1/reference/html/native-image.html
А что делать если в момент сборки еще не ясно - нужен native image или нет? Или нужны обе версии? Нет проблем - можно совместить оба режима JIT и AOT и создать артефакт, Spring Boot Executable Jar, с байткодом и всеми необходимыми для native image метаданными. И собрать из него native image позже в DevOps pipeline при необходимости.
Для Spring Boot есть два режима сборки. Основной - Native Image Using Buildpacks, в котором в итоге получается docker образ. Для него нужен только Docker на машине-сборщике. И т.наз. Native Build Tools - нужно устанавливать дистрибутив GraalVM, содержащий эти tools, в итоге получается бинарник для железа, на котором происходит сборка.
Итого - штука полезная, но только если вас категорически не устраивает время запуска приложения и все используемые вами фреймворки поддерживают native image.
#jvm #performance #native_image #spring #docker #buildpacks #cloud #startup_time
www.graalvm.org
GraalVM
GraalVM is an advanced JDK with ahead-of-time Native Image compilation.