VJMap для разных поколений (нового поколения, живущей зоны, старого поколения) — это инструмент для поиска причин медленного роста утечек памяти. Поскольку jmap -histo PID
печатает статистическую информацию об объектах всего Heap, нам нужно специально изучить объекты OldGen и долгоживущие объекты в зоне Survivor, чтобы определить проблему.
Первоначальная идея VJMap основана на RTBJMap от Alibaba, который был обновлён для поддержки JDK8, фильтрации объектов в зоне Survivor и избегания проблем с выводом результатов.
Здесь есть практический пример: «[Только практика] Поиск проблем с быстрым ростом старого поколения JVM». В итоге проблема была связана с Jedis.
Обратите внимание: из-за принципа работы VJMap он поддерживает только CMS и ParallelGC, но не G1.
Скачать vjmap-1.0.8.zip (из Maven Central).
JAVA_HOME используется VJMap как JAVA_HOME/bin/java. Требуется как минимум JDK7, и JVM приложения должна использовать ту же версию JDK.
VJMap зависит от JAVA_HOME/lib/sa-jdi.jar.
Путь к JAVA_HOME определяется через переменную среды JAVA_HOME или, если она не определена, через команду «which java» для получения относительного пути.
Для выполнения требуются права root (sudo -E vjmap.sh ...), аналогичные правам jmap -heap pid.
Если соединение с процессом невозможно, можно попробовать выполнить jstack -F pid и jmap -heap pid для сравнения.
При работе в контейнере требуется разрешение ptrace.
Для активных процессов используйте PID для идентификации процесса.
// Вывести полную статистику объектов в куче, отсортированную по общему размеру объекта:
./vjmap.sh -all PID > /tmp/histo.log
// Рекомендуется: вывести статистику объектов старого поколения, отсортированных по размеру oldgen, быстрее, чем -all, в настоящее время поддерживает только CMS:
./vjmap.sh -old PID > /tmp/histo-old.log
// Рекомендуется: вывести статистику объектов зоны Survivor, по умолчанию age>=3:
./vjmap.sh -sur PID > /tmp/histo-sur.log
// Рекомендуется: вывести статистику объектов зоны Survivor с минимальным возрастом 4:
./vjmap.sh -sur:minage=4 PID > /tmp/histo-sur.log
// Рекомендуется: вывести статистику объектов зоны Survivor только с возрастом 4:
./vjmap.sh -sur:age=4 PID > /tmp/histo-sur.log
Для файлов CoreDump:
./vjmap.sh -old ${path_to_java} ${path_to_coredump}
Принцип заключается в выполнении полного GC перед официальной статистикой.
./vjmap.sh -old:live PID > /tmp/histo-old-live.log
// Отфильтровать по размеру oldgen: отображать только объекты, занимающие более 1K в OldGen:
./vjmap.sh -old:minsize=1024 PID > /tmp/histo-old.log
./vjmap.sh -all:minsize=1024,byname PID > /tmp/histo.log
Выполнение VJMap занимает некоторое время. Если необходимо остановить его в середине процесса, используйте Ctrl+C или убейте PID VJMap. Если вы случайно использовали kill -9, целевой процесс Java останется заблокированным и больше не будет работать. В этом случае дважды выполните kill -SIGCONT $target_process_PID, чтобы перезапустить целевой процесс Java.
Если давно не было полных или CMS GC, в OldGen будет много живых регионов. Выполнение -all и -old может занять много времени. Например, первая часть -all Get Live Regions может быть очень медленной. Если не требуется намеренно наблюдать за мёртвыми объектами, сначала можно запустить полный GC, например, используя vjmap -all:live, или jmap -histo:live, или jcmd GC.run.
Survivor Object Histogram:
#num #count #bytes #Class description
-----------------------------------------------------------------------------------
1: 37 1k io.netty.buffer.PoolThreadCache$MemoryRegionCache$Entry
2: 2 64 java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
Total: 39/ 1k over age 2
Траверсирование кучи заняло 1,3 секунды.
Если информации об объектах недостаточно для определения проблемы, необходимо использовать полный дамп Heap и анализировать отношения объектов для дальнейшего анализа. В MAT можно использовать OQL для фильтрации объектов старого поколения.
Предположим, что диапазон адресов OldGen составляет «0xfbd4c000» – «0xfce94050».
SELECT * FROM INSTANCEOF java.lang.Object t WHERE toHex(t.@objectAddress) <= "0xfce94050" AND toHex(t.@objectAddress) >= "0xfbd4c000"
Примечание: в настройках предпочтений MAT необходимо включить «Сохранять недоступные объекты».
Чтобы получить адрес старого поколения, можно сделать следующее:
Добавить параметр запуска -XX:+PrintHeapAtGC, который будет печатать адрес при каждом GC.
Использовать команду -address в VJMap, которая также будет печатать этот диапазон адресов.
Быстро распечатать адреса всех поколений с помощью команды address в VJMap без длительной паузы.
./vjmap.sh -address PID
Вывод:
eden [0x0000000119000000,0x0000000119c4a258,0x0000000121880000) space capacity = 143130624, 9.003395387977907 used
from [0x0000000121880000,0x0000000121880000,0x0000000122990000) space capacity = 17891328, 0.0 used
to [0x0000000122990000,0x0000000122990000,0x0000000123aa0000) space capacity = 17891328, 0.0 used
concurrent mark-sweep generation
free-list-space[ 0x0000000123aa0000 , 0x0000000139000000 ) space capacity = 357957632 used(4%)= 17024696 free= 340932936
В этом примере 0x123aa0000 является нижним пределом OldGen. Обратите внимание, что при использовании OQL необходимо удалить начальную строку чисел перед «0x».
./vjmap.sh -class PID
Чтобы соответствовать JDK8, информация о классе в Jar-файле больше не печатается.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )