мониторинг выполнения и оптимизация приложений java
Скахин Алексей / pihel
Личный блог. Заметки о программировании и не только
Страницы
четверг, 21 ноября 2019 г.
Настройка и мониторинг производительности Java приложений
Состав кучи
Параметры настройки
Полное описание всех параметров на сайте oracle.
Значения, выставляемые по умолчанию:
* Для серверного ПК:
— Parallel (Throutput) collector
— Xms (начальный размер Heap) = 1/64 доступной памяти
— Xmx (максимальный размер Heap) = 1/4 памяти
* Для прочих устройств:
— Serial gc
— Xm* параметры такиеже
Вариант оптимизации: устанавливать Xms = Xmx, чтобы ускорить работу программы, за счет уменьшения числа изменений объема памяти
Виды сборщиков мусора
Serial
Parallel
Parallel Old
Такой же как «Parallel», но Old GC выполняется параллельно, что еще добавляет пропускной способности.
Над Old областью также происходит дефрагментация данных, что упрощает поиск свободных мест для будущих объектов:
Над Yang областью не надо проводить дефргаментацию, т.к. она заполняется перемещением между S0 и S1.
Concurrent Mark-Sweep
Этот коллектор используется по умолчанию в серверных приложениях.
По принципу работы G1 схож с CMS, но имеет ряд преимуществ:
* Выполняется конкурентно как CMS gc
* Память дефрагментируется без остановки основного цикла программы
* Фиксированный (предсказуемый) размер паузы gc
* Пропускная способность остается достаточно высокой
* Не требует дополнительных 20% озу, как CMS gc (но все равно нужны 10% = G1ReservePercent)
Достигается это за счет того, что вся память делится на 2048 регионов (-XX:G1HeapRegionSize) от 1 до 32 МБ (в зависимости от размера кучи).
Выжившие объекты копируются из своего региона в свободный (дефрагментация), этот процесс дискретный и может быть остановлен в любой момент (фиксированная пауза)
Желатльно чтобы размер объекта был от 50-100% размера региона. Если объект больше региона, то он будет хранится в смежных регионах, что может вызывать задержки при обработке.
Параметры коллектора, на которые стоит обратить внимание:
-XX:MaxGCPauseMillis=200
Ставьте этот параметр = 90% от необходимого времени отклика
-XX:ConcGCThreads
кол-во процессов параллельной обработки регионов
-XX:InitiatingHeapOccupancyPercent=45
заполненность heap, после которой начинается gc
-XX:G1ReservePercent=10
резерв памяти, для защиты от переполнения
Полный список параметров и их описание на сайте oracle
GC в Hive
В сборке Hadoop от Hortonworks используется G1 gc для Hive
Сменить GC можно, только с перезапуском сервиса.
В виду сложности этой операции, проведем тесты с параметрыми G1, которые можно менять прямо во время выполнения запроса.
Протестируем параметры gc на запросе с distinct, чтобы был создан большой хэш массив и сортировок со слиянием.
Комманды для мониторинга Java из консоли
PrintCommandLineFlags
Verbose: gc
HeapDumpOnOutOfMemoryError
JSTAT
VisualVM
Удобное бесплатное приложение от Oracle для анализа Java приложений.
VisualVM расширяется за счет plugins. Основные, которыми рекомендую пользоваться:
Топ объектов по памяти, без привязки к методам:
Но тут же можно снять heap dump и узнать реальную картину распредления памяти по методам и объектам:
Если это оконное java приложение, то сэмплировать можно выбранную часть окна:
Профилирование
Профилировщик явно трассирует работу программы, что дает наиболее верный резальтат в сравнении с сэмплированием.
Для того, чтобы начать трассировать программу, нужно запустить ее с особенными параметрами: После чего к запущенному приложению можно подключиться для анализа из visualvm.
При запуске можно указать фильтр на определенный класс приложения.
В отличие от сэплирования, при трассировке, объем объектов можно анализировать в разрезе методов:
Так же можно анализировать исходящие JDBC запросы:
Распределение времени CPU:
Flight Recorder
Mission Control
Приложение для анализа трассировок записанных Flight Recorder (.jfr)
Общая информация о системных показателях: CPU, Memory:
Топ объектов в heap (можно получить и разрез по методам):
Распределение времени по объектам:
Но, похоже, статистики по JDBC нет.
Мониторинг OS
Мониторинг CPU, ОЗУ * Procs
r: число процессов ожидающих выполнение
b: число спящих процессов
* Memory
swpd: зарезервировано виртуальной памяти
free: свободной памяти
buff: памяти для буферов
cache: памяти под кэш
* Swap
si: памяти загруженной в озу с диска / сек
so: памяти выгруженной на диск / сек
* IO
bi: число считанных блоков / сек
bo: число записанных блоков / сек
* System
in: плановая смена контекста на новый поток
cs: внеплановая смена контекста, когда потом с большим приоритетом вытесняет низкоприоритетный
* % общего времени CPU
us: время за работой программ
sy: время работы ОС. Большой % тут означает большое число блокировок при доступе к ресурсам.
id: Время Idle
wa: время ожидания IO
st: Время виртуализации
Top CPU процессов
включая строку запуска процесса Потоки процесса
Получить список потоков, отсортированных по потреблению CPU: В 4 столбце «LWP» находится ID потока.
Если сделать theatdump процесса через jstack, то можно найти проблемный поток, который грузит CPU.
Для этого LWP нужно преобразовать из десятичной системы в шестнадцатеритную и найти поток с этим nid (0x3d4a == 15690)
Flame Graph
Я бы хотел показать как создать Flame Graph для Java приложения, т.к. есть особенности:
1. JVM должна быть запущена с флагом «-XX:+PreserveFramePointer», который поддерживается с JDK 8u60
2. Нужно скачать и скомпилировать утилиту perf-map-agent
Она нужна, чтобы получить читаемые названия функций.
3. Скачать Perl скрипты FlameGraph
4. Установить пакет perf в Linux
Пример запуска Spark с параметром PreserveFramePointer Запуск Hive с параметром PreserveFramePointer Далее запускаем проблемный запрос в Hive/Spark.
Подключаемся по ssh к одной из нод кластера.
Ищем нужные процессы с запросом (см. п. Мониторинг OS)
Примеры полных FlameGraph для Hive и Spark
Оптимизация работы с памятью в программах
Примитивы
* Используйте литералы, вместо создания строк
* Используйте глобальную видимость объектов, чтобы их переиспользовать
* Примитивы работают быстрей, чем объектные аналоги
* Условия проверки быстрей, генерации и отлова исключений
* Большое кол-во synchronization блокировок замедляет программу
* Используйте копирование памяти кусками (arraycopy), вместо построчного
Особенности JVM IBM AIX
* Активируются расширенные логи GC аналогично:
Но ведутся в формате xml. Визуализировать gc log проще всего тут
Jstat для online просмотра не поддерживается.
* Flight Recorder не поддерживается этой JVM. Для мониторинга работы нужно использовать Healthcenter
Запуск online мониторинга: Накладные расходы при online = 0.4%, при записи в файл = 1%
Просмотр мониторов происходит через Health Center для Eclipse
* Для возможности снятия дампов нужно активировать опцию Xdump:heap После этого дамп можно снять консольной командой: Последующий анализ дампа нужно проводить в MemoryAnalyzer плагине для Eclipse
Передача команд через JCMD не поддерживается.
* Также активировать Heap Dump, Трассировку и VerboseGc можно из Health Center для Eclipse при активированном online мониторинге.
Как отслеживать использование памяти CPU и загруженность диска в Java
В этой статье, мы обсудим некоторые начальные команды, инструменты и методы, которые помогут отслеживать использование памяти CPU и загруженность диска в Java.
Java инструменты наблюдают за конструкциями и процессами байткода Java. Профайлеры Java следят за всеми системными командами и использованием процессора. Это позволяет вам вызвать средства в любой момент.
Здесь мы перечислили способы демонстрации памяти и отслеживания CPU в Java для оптимизации кода.
5 способов для наблюдения за памятью CPU и загруженностью диска в Java!
Базовые Linux команды для отслеживания использования памяти CPU и загруженности диска
a) free –m
Команда передает доступную и занятую память вашей виртуальной машины.
b) top
Эта команда для просмотра использования CPU и памяти.
Когда вы введете 1 на клавиатуре, тогда top сработает и отобразит все возможные CPU и использование каждого.
c) meminfo и cpuinfo
Используйте следующие команды для детальной информации относительно памяти и процессоров виртуальной машины.
$cat /proc/cpuinfo
$cat /proc/meminfo
d) Память CPU и использование диска конкретного процесса.
Эта команда для отслеживания использования CPU и загруженности памяти методом приложения Java. Также отображает какие команды запустили процесс.
e) Это статистика потока Java метода.
$ps uH –p [pid]
Эта команда предоставляет число потоков в процессе и их статистику.
Это показывает как много памяти и CPU использовано для каждой угрозы и как долго они работают.
Когда приложение не отвечает, эти команды могут использоваться для выполнения предыдущих запросов, прежде чем переходить к памяти и дырам в потоках. Это важные команды для первоначального исследования кроме статистики памяти и CPU.
df –k –> Для проверки места на диске
–> Для проверки, является ли данный порт открытым
lsof –i: tcp –> Все установленные и слушающие TCP подключения
Отслеживание использования памяти
Далее рассмотрим класс ManagementFactory для отслеживания использования памяти. Это реализовано путем запроса к MemoryMXBean. Раздел памяти куча необходим для использования MemoryMXBean:
Это пример возвращает исходную, используемую, максимальную и переданную память. Здесь идет описание каждой:
Исходная: исходный концепт Java приложений из ОС в течение запуска
Используемая: память, используемая Java
Максимальная: большая часть памяти относится к JVM. OutOfMemoryException возникает, если память заполнена
Переданная: объем памяти доступный JVM
Нативный агент профилирования (-Agentpath)
После того как нативная библиотека инструментов спроектирована на удаленном сервере, интерфейс пользователя соединен с удаленным сервером для профилирования.
Инструменты такие как JProfiler и Yourkit используют нативные инструменты профилирования.
Java агент профилирования (-Javaagent)
a) Оснащение программы требует разработки premain() системы
b) Класс программы должен быть спроектирован как Pre-Main в MANIFEST.MF записи в данных JAR, где программа и ее зависимые классы создаются
c) JVM начинается с –javaagen
Если инструменты системы ожидают агент, для расположения после main() системы приложения, следующий шаг получить agentmain() метод в другом агент классе. Это следует сконструировать как Agent-Main класс в MANIFEST.MF записи.
Агент библиотека (-Agentlib)
2) Для контроля JVM в режиме отладки
1) Использование HPROF:
HPROF профилируют кучу и CPU, которые передаются рядом с Java. Это делает язык эффективным. –agentlib:hprof и –Xrunhprof обычно используются как параметры виртуальной машины с HPROF.
JVM отслеживает данные относящиеся к профилям кучи, обозначенных концептов, трассировка стека в java.hprof.txt.
java agentlib:hprof=heap=sites Hello.java
HPROF может быть покрыт во время сборки, так разработчик может выявить серьезные проблемы.
2) Запуск приложения в режиме отладки
Запуск приложения в режиме отладки подтверждается частью –agentlib:jdwp виртуальной машины. Это решение размещает внутрипроцессные архивы отладки JVM
Этот параметр поддерживает отладку в приложении, использующим IDE.
Простые шаги
Используйте команды ограниченно/удаленно для метода отладки в Java. Приложение, которое отслеживает записи на указанном порту не переместится дальше пока шаг (ii) не завершится.
Скриншот записи из приложения: Monitoring for transport dt_socket at address: 9001
Инициализируйте проект в IDE в режиме отладки и присоедините шифрование к хосту и порту.
Отлаживайте код на единственном сервере с IDE.
Full-stack мониторинг на примере Java приложений
Всем привет! За последние пять лет мы достаточно часто сталкивались с запросами на мониторинг Java приложений. Собрав основные моменты по мониторингу Java, мы решили написать данный пост.
Сегодня мы с вами рассмотрим, что такое full-stack мониторинг и чем он отличается от привычного «уху» понятию мониторинга, нюансы full-stack мониторинга для Java и сложности мониторинга микросервисных приложений на Java. Расскажем, как мы реализуем full-stack мониторинг с помощью open-source стандартов и платной платформы.
Full-stack мониторинг
Давайте определимся, что мы называем full-stack мониторингом?
Full-stack мониторинг — это подход в мониторинге производительности приложений, который подразумевает под собой мониторинг всего стека, что включает в себя:
Мониторинг приложений — сбор метрик приложения, сбор трейсов транзакций, обеспечение видимости на уровне кода и т.д.
Мониторинг инфраструктуры — метрики хостов, процессов, контейнеров и т.д.
Мониторинг конечных пользователей — сбор метрик с браузера пользователя, мобильного приложения, синтетические проверки и т.д.
Основное отличие full-stack мониторинга от привычного мониторинга заключается в том, что, применяя full-stack мониторинг, мы получаем реальное представление о том, как себя «чувствует” наше приложение и как его работа влияет на пользовательский опыт, так как видим не просто метрики приложения, а влияние всех инфраструктурных компонентов на приложение и то, как это сказывается на опыте наших пользователей.
Зачастую под full-stack мониторингом подразумевают продукт/решение, который предоставляет единый сервис для сбора, хранения и анализа собираемых данных с приложения, инфраструктуры и пользователей. Но в нашей статье мы поговорим именно про подход и расскажем про использование как open-source стандартов, так и платной платформы для реализации full-stack мониторинга Java.
По результатам нашего опыта можем сказать, что применение full-stack подхода к мониторингу позволяет заметно ускорить процесс root cause анализа инцидентов и действительно снизить MTTR.
Нюансы full-stack мониторинга Java
Чтобы понимать что нужно мониторить в Java приложениях, давайте для начала разберемся что собой представляет Java. Во-первых, Java — это приложения, написанные не только на языке программирования Java, но и на других языках, которые основываются на Java – Kotlin, Scala, JRuby, и другие.
Во-вторых, Java — это еще и среда для чтения, понимания и выполнения байт-кода Java. Приложения, написанные на Java языке или на языках, базирующихся на Java, компилируются в байткод и запускаются в определенной среде исполнения – JVM (Java Virtual Machine).
Поэтому нам нужно мониторить как само приложение на Java, так и среду, в которой оно запускается.
В целом full-stack мониторинг Java будет выглядеть следующим образом:
Сбор Java и JVM метрик.
Сбор распределенных трассировок транзакций.
Профилирование Java кода.
Мониторинг конечных пользователей.
Давайте подробнее пройдемся по всем компонентам full-stack мониторинга Java.
Сбор метрик Java и Java Virtual Machine
Под сбором метрик Java подразумевается сбор уникальных метрик с используемых фреймворков Java (SpringBoot, DropWizzard и т.д.) и applications серверов (JBoss/Wildfly, Websphere и т.д.), например:
количество HTTP Requests для SpringBoot Framework;
количество WebDeployments и количество их активных сессий для JBoss;
количество запросов и среднее время отклика servlets для Websphere.
Получить метрики Java Virtual Machine (JVM) можно с помощью имеющейся у Java технологии JMX (Java Management Extensions), которая представляет информацию о состоянии самой JVM, garbage collection и других внутренних элементов.
Какие метрики JVM нас интересуют в первую очередь:
Memory Usage
Общий объем памяти, используемый самой JVM.
Получим из java.lang.Runtime#totalMemory
Текущие идентификаторы тредов получим из java.lang.management.ThreadMXBean#getAllThreadIds
Статус тредов получим из ThreadMXBean#getThreadInfo
Heap Memory
Общее использование heap memory самой JVM.
Максимальное значение получим из java.lang.Runtime#maxMemory
Memory Pools
Использование memory pools
Информацию о пуле получим из ManagementFactory#getMemoryPoolMXBeans
Значение использования памяти пулом получим из java.lang.management.MemoryUsage
Garbage Collection
Значение garbage collection и время его выполнения
Информацию о garbage collection получим из ManagementFactory#getGarbageCollectorMXBeans
Значение для каждого коллектора получим из java.lang.management.GarbageCollectorMXBean
Garbage collection runtime получим из getCollectionTime
Во время разработки приложений, самыми популярными инструментами для сбора и анализа метрик являются VisualVM и JDK Mission Control.
Помимо стандартного набора метрик, можно описать свои собственные (кастомные) метрики и здесь нам помогут такие инструменты, как DropWizard, Micrometer, StatsD и Prometheus.
На нашей практике, чаще всего используется Prometheus для описания кастомных метрик. Prometheus предоставляет большой набор интеграций и оберток для большинства популярных фреймворков, основанных на Java. Благодаря чему, Prometheus достаточно просто и быстро интегрируется с приложением.
У всех вышеупомянутых инструментов есть один большой недостаток – метрики изолированы друг от друга из-за чего отсутствует контекст данных, который нам необходим для расследования инцидентов, особенно если у нас микросервисная среда. Но к этому мы вернемся чуть позже, а пока перейдем к трейсингу запросов.
Сбор распределенных трассировок (distributed tracing)
Чтобы обеспечить быстрое выявление и решение проблем с Java приложениями, одного сбора метрик будет недостаточно, необходимо видеть, как все наши сервисы взаимодействуют между собой, то есть собирать информацию о наших транзакциях. Это возможно сделать с помощью end-to-end трейсинга всех вызовов в нашем Java приложении.
Наиболее распространенный путь в получении end-to-end трейсинга — это использование open-source стандартов, например, таких как, OpenTelemetry и OpenTracing.
Пример видимости транзакции в одном из самых распространенных open-source инструментов для end-to-end трейсинга – Jaeger
Поэтому для сбора метрик, транзакций и их корреляции между собой в микросервисных инфраструктур, мы используем готовые продукты для full-stack мониторинга, ниже расскажем про их применение.
Профилирование Java кода
Чтобы понять причину, почему наше Java приложение потребляет много ресурсов, нам нужно видеть, какой именно метод приложения вызывает проблемы с производительностью. Поэтому нужно производить профилирование Java кода.
Профилировщики существуют уже очень давно. Первые профилировщики использовались только на этапе отладки приложения, в тестовой среде, из-за их высокого потребления CPU и Memory ресурсов. Но сейчас существуют профилировщики, которые не несут такой высокой нагрузки, и мы можем их запускать уже в продакшен среде.
На нашей практике, для профилирования приложения в основном используются такие инструменты, как VisualVM, jProfiler и JavaMissionControl в сочетании с Flight Recorder.
Мониторинг конечных пользователей (End user мониторинг/Real user мониторинг)
Как мы писали выше, одной из важных частей full-stack мониторинга является мониторинг наших конечных пользователей. Это обусловлено тем, что работа нашего приложения напрямую влияет на пользовательский опыт. Пользователи взаимодействуют с приложением через веб-сайт или мобильное приложение и понимание того какие запросы от пользователей уходят на бэкенд, т.е. запросы к Java приложению, обеспечивают сквозную видимость запроса, от «клика» пользователя в браузере или мобильном приложение до конкретной транзакции на бэкенде.
Таким образом, мы видим влияние скорости работы и ошибок в Java приложение на пользовательский опыт.
Мы решили сейчас много не писать на тему end user мониторинга так как мы ранее уже достаточно подробно раскрыли эту тему в одном из наших постов:
Сложности мониторинга микросервисных Java приложений
Не могли не затронуть, хотя бы вскользь, тему мониторинга микросервисных Java приложений.
Основная сложность мониторинга таких приложений заключается в том, что увеличивается количество подконтрольных компонентов – контейнеры, оркестраторы (k8s, docker swarm, openshift и т.д). Контейнеры с приложениями постоянно скейлятся, могут запускать только на короткий срок времени. Все это экспоненциально усложняет процесс мониторинга.
Под автодискаверингом принято понимать процесс непрерывного, автоматического обнаружения компонентов инфраструктуры с целью сбора ключевых данных о каждом из компонентов. В зависимости от типа компонента – приложение, база данных, балансировщик, очередь и т.д., данные будут собираться разные, но важно, чтобы они связывались между собой.
Визуализация стека компонентов с их ключевыми метриками в продукте Instana
Отсутствие автодискаверинга как неотъемлемой части full-stack мониторинга микросервисных приложениях приводит к тому, что вновь запущенные компоненты, могут быть не «замониторены» и в результате у нас появятся слепые зоны в инфраструктуре. Мы не будем иметь представления о том, как себя действительно «чувствует» тот или иной компонент инфраструктуры, не сможем корректно оценить состояние приложения.
Реализовать подход автодискаверинга вручную, достаточно трудоемкий процесс, поэтому в наших проектах мы используем готовое решение – Instana.
Full-stack мониторинг как продукт
Выше мы с вами рассмотрели full-stack мониторинг как подход, здесь же мы поговорим о full-tack мониторинге как готовом к использованию продукте.
С помощью Instana мы получаем full-stack мониторинг в том числе и для Java приложений без необходимости ручной настройки.
Instana постоянно и автоматически собирает ключевые данные для поддерживаемых технологий, относительно JVM обнаруживает все что находится внутри нее:
коннекторы к базам данным;
Upstream и Downstream сервисы;
Причем для агента Instana не важно в какой инфраструктуре запущено приложение – в Docker, Kubernetes, OpenShift, Linux или Windows. После обнаружения JVM агент подключается к самому процессу и анализирует архитектуру сервиса, какой Framework используется (Spring Boot, DropWizzard и так далее), сервер приложений (Jboss/Wildfly, Websphere, и тд) и коннекторы к базам данных. Далее приложение автоматически инструментируется и агентом начинают собираться ключевые метрики и трейсы приложения. Очень важно, что при этом не требуется перезапуск приложения.
Понимание топологии приложения позволяет Instana построить логическую модель всего стека, обнаружить сервисы и их зависимости.
Давайте посмотрим на пример топологической модели Spring Boot приложения:
Представив топологию в виде вертикального стека зависимостей компонентов инфраструктуры, с каждого уровня необходимо собрать уникальные метрики. И Instana собирает метрики с каждого уровня и визуализирует весь стек.
Но как мы писали выше, метрик недостаточно для полноценного мониторинга Java приложений. Приложения предоставляют собой сервисы, которые между собой как-то взаимодействуют. Instana обнаруживает все вызовы между сервисами и создает распределенные трейсы, показывая end-to-end карту вызовов.
Посмотрев на карту вызовов, возникает необходимость проанализировать трейсы проблемного сервиса. И мы можем более детально проанализировать каждый из вызовов. Instana не сэмплирует вызовы, что позволяет анализировать абсолютно каждый из них.
Важной частью мониторинга Java приложений является профилирование кода. У нас могут быть метрики всех компонентов приложения и видимость всех вызовов, и мы можем понять связана ли эта проблема с использованием ресурсов и какой сервис именно пострадал. Но мы не узнаем, какой именно метод в работающем сервисе приложения вызывал проблемы с производительностью. И тут нам поможет постоянно работающий Java профилировщик. Профилировщик покажет точный метод(ы), которые перегружают CPU или имеют большой wait time.
Подводя итоги
Актуальность full-stack мониторинга будет только расти и это обусловлено, в том числе, переходом на микросервисную архитектуру, которая экспоненциально усложняет мониторинг по причине постоянного увеличения подконтрольных компонентов инфраструктуры.
Full-stack мониторинг позволяет охватить весь наш стек – приложения, инфраструктуру, пользователей, и значительно ускорить root-cause анализ и снизить MTTR.
Имплементировать full-stack мониторинг возможно и как с помощью подхода “сделай сам”, используя open source стандарты (Prometheus/statsD для сбора метрик, Jaeger/Zipkin для сбора трейсов, jProfiler/Java VisualVM для профилирования Java кода), так и с помощью готовых продуктов, которых на рынке сейчас уже не мало.
Будем рады, если в комментариях вы поделитесь применяете ли full-stack мониторинг, как мониторите Java приложения и какие используете для этого инструменты?