Для приложений на основе контейнеров сложно обнаружить проблемы, связанные с чрезмерным использованием памяти. В случае, если потребление превышает лимит памяти контейнера, приложение может тихо отказаться без оставления каких-либо следов.
В данной статье я рассмотрю некоторые методы, которые можно использовать для выявления источника потребления памяти в приложении на Java, работающем в контейнере.
Тип памяти
В типичном приложении на Java, память можно разделить на кучу и некучу. Размер кучи памяти можно установить, указав соответствующие параметры JVM при запуске любого приложения на Java.
Некучая память включает в себя системную память, используемую самим JVM или любым библиотечным приложением, использующим JNI (Java Native Interface).
Метод
Для кучи памяти можно взять дамп кучи и проанализировать его с помощью инструментов анализа дампа кучи. Одним из лучших инструментов для анализа дампа кучи является eclipse MAT.
Java предоставляет механизм отслеживания выделения системной памяти путем включения отслеживания системной памяти, но он может не раскрывать всю память, выделенную системными библиотеками.
Jemalloc — это инструмент, который можно использовать для отслеживания памяти, выделенной нативной библиотекой. Нативная память выделяется с использованием стандартного распределителя памяти под названием malloc. Jemalloc — это универсальное реализация malloc, с помощью которой можно включить отслеживание выделения памяти. Он отслеживает все выделение памяти и генерирует дампы профилей кучи.
Эти профили кучи затем можно анализировать с помощью Jeprof инструмента. Jeprof создает отчет о выделении кучи, выделяя память, используемую функциями в приложении.
Анализ
Ниже представлен анализ памяти для примера контейнера Java. Приложение загружает образец Tensorflow модели для обеспечения использования нативной памяти и работает в контейнере Docker.
Ниже представлено потребление памяти Docker. Оно составляет 254 МБ. Попробуем точно определить источник потребления памяти.
Общий объем памяти
Чтобы получить представление о общем объеме памяти, используемой процессом приложения, можно проверить Resident Set Size (RSS). Это общий объем зарезервированной памяти, которая находится в основной памяти или RAM. Существует несколько инструментов, которые могут помочь проверить это, например top, ps или pmap.
Проверка RSS не помогает точно определить источник использования. Для примера приложения, используя следующую команду, общий RSS составляет 376MB.
ps --no-header -o rss $(pidof java)
Анализ кучи
Ниже представлено потребление памяти кучи, созданное инструментом eclipse MAT. Общая удерживаемая куча показана как 2,2MB, что значительно ниже общего потребления памяти, показанного Docker, и указывает на то, что большая часть потребления приходится на некучевую область.
Анализ нативной памяти
При просмотре сводки о нативной памяти с помощью следующей команды, общее использование памяти кажется примерно 99MB. Однако это значение меньше общего потребления памяти и не точно определяет коренную причину проблемы.
jcmd $(pidof java) VM.native_memory \
| grep -P "Total.*committed=" \
| grep -o -P "(?<=committed=)[0-9]+(?=KB)"
Анализ внешней кучи
Анализ с использованием Jemalloc и Jeprof показывает, что использование нативной памяти в основном связано с библиотекой Tensorflow, с общим потреблением около 112MB.
Этот взгляд дает четкое указание на источник использования нативной памяти и может быть дополнительно исследован для минимизации чрезмерного потребления.
Заключение
Анализ памяти Java критически важен, особенно для приложений на основе контейнеров. Зная источник потребления памяти в приложении, можно понять требования к памяти и снизить стоимость приложения за счет удаления ненужного потребления.
При проверке потребления памяти необходимо точно определить все типы памяти и их источники. Анализ дампа кучи может определить источники потребления кучи, а Jemalloc и Jeprof полезны для определения источников потребления нативной памяти.
Ссылка на образец прикладного кода
Source:
https://dzone.com/articles/java-container-application-memory-analysis