Что потребляет память в процессе Java?


18

мы пытаемся исследовать использование памяти процессом Java при умеренной нагрузке.

  PID   USER    PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  12663 test    20   0 8378m 6.0g 4492 S   43  8.4 162:29.95 java

Как вы можете видеть, у нас есть резидентная память на 6Gb. Теперь интересная часть заключается в следующем: процесс выполняется с этими параметрами:

  • -Xmx2048m
  • -Xms2048m
  • -XX: NewSize = 512m
  • -XX: MaxDirectMemorySize = 256m
  • ... некоторые другие для GC и прочее

Глядя на эти настройки и на фактическое использование памяти, мы сталкиваемся с разницей в том, что мы ожидаем, что этот процесс будет использовать и что он на самом деле использует.

Обычно проблемы с памятью решаются путем анализа дампа кучи, но в этом случае наша память используется где-то за пределами кучи.

Вопросы: Каковы были бы шаги, чтобы попытаться найти причину такого высокого использования памяти? Какие инструменты могут помочь нам определить, что использует память в этом процессе?

РЕДАКТИРОВАТЬ 0

Не похоже, что это проблема, связанная с кучей, поскольку у нас все еще есть достаточно места:

jmap -heap 12663

результаты (отредактировано для экономии места)

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize      = 2147483648 (2048.0MB)
NewSize          = 536870912 (512.0MB)
MaxNewSize       = 536870912 (512.0MB)
OldSize          = 1610612736 (1536.0MB)
NewRatio         = 7
SurvivorRatio    = 8
PermSize         = 21757952 (20.75MB)
MaxPermSize      = 85983232 (82.0MB)

New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used

РЕДАКТИРОВАТЬ 1

используя pmap, мы можем видеть, что существует довольно много выделений 64 Мб:

pmap -x 12663 | grep rwx | sort -n -k3 | less

результаты в:

... a lot more of these 64Mb chunks
00007f32b8000000       0   65508   65508 rwx--    [ anon ] <- what are these?
00007f32ac000000       0   65512   65512 rwx--    [ anon ]
00007f3268000000       0   65516   65516 rwx--    [ anon ]
00007f3324000000       0   65516   65516 rwx--    [ anon ]
00007f32c0000000       0   65520   65520 rwx--    [ anon ]
00007f3314000000       0   65528   65528 rwx--    [ anon ] 
00000000401cf000       0  241904  240980 rwx--    [ anon ] <- Direct memory ?
000000077ae00000       0 2139688 2139048 rwx--    [ anon ] <- Heap ?

Итак, как узнать, что это за куски по 64 Мб? Что их использует? Какие данные в них?

Спасибо


2
У меня точно такая же проблема ... вот мой вопрос. stackoverflow.com/questions/18734389/… У вас есть решение по этому поводу?
DeepNightTwo

Ответы:


21

Проблема может быть связана с этой проблемой glibc .

По сути, когда у вас несколько потоков, выделяющих память, glibc будет увеличивать количество доступных арен для распределения, чтобы избежать конфликта блокировок. Размер арены составляет 64 МБ. Верхний предел состоит в том, чтобы создать в 8 раз больше ядер ядер. Арены создаются по требованию, когда поток получает доступ к уже заблокированной арене, поэтому со временем он увеличивается.

В Java, где вы поливаете потоками, это может быстро привести к созданию множества арен. И все эти ассигнования распространились по всем этим аренам. Первоначально каждая 64-мегабайтная арена представляет собой просто неиспользуемую память, но когда вы делаете выделения, вы начинаете использовать для них реальную память.

Ваш pmap, вероятно, имеет списки, похожие на это ниже. Обратите внимание, что 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. То есть они составляют до 64Мб.

00007f4394000000 324 тыс. Тонн --- [скоро]
00007f4394051000 65212K ----- [anon]
00007f4398000000 560 тыс. Тонн --- [скоро]
00007f439808c000 64976K ----- [anon]
00007f439c000000 620 тыс. Тонн --- [скоро]
00007f439c09b000 64916K ----- [anon]

Что касается обходных путей : в ошибке упоминаются некоторые параметры среды, которые вы можете установить для ограничения количества арен, но вам нужна достаточно высокая версия glibc.


5
установка Xmx и Xms на одно и то же значение, плюс установка переменной окружения "export MALLOC_ARENA_MAX = 4" в скрипте sh, который запускает наш веб-сервис, в нашем случае помог. До этого мы испытывали перезапуски веб-сервисов из-за OOM Killer каждые 2-8 часов. Версия GLIBC в Ubuntu 14.04 - 2.19, что хорошо, так как она должна быть> = 2.16, чтобы настройка MALLOC_ARENA_MAX работала
Kluyg,

Этот ответ и приведенный выше комментарий стали для меня спасением. В моем случае MALLOC_ARENA_MAX = 1 было необходимо и эффективно.
Джон Башир

3

Как насчет Lamdba Probe ? Среди прочего он может показать вам разбивки использования памяти, подобные скриншоту ниже:

Вид использования памяти лямбда-зондом

Иногда pmap -x your_java_pidтакже может быть полезным.


Спасибо за Ваш ответ. Если я правильно понимаю, то лямбда-зонд для Apache Tomcat? Которые мы не используем ... Что касается pmap, я добавлю информацию в верхний пост
Константин С.

2

JProfiler может быть чем-то, что вы ищете, но это не бесплатно. Другим хорошим и бесплатным инструментом для исследования использования памяти процессом Java является Java VisualVM, доступный в качестве инструмента JDK в дистрибутивах Oracle / Sun JDK. Я лично рекомендую более целостный подход к проблеме (например, для мониторинга дисков JDK + OS + и т. Д.) - использование некоторой системы сетевого мониторинга - Nagios, Verax NMS или OpenNMS.


2
Его утечка вне кучи, так что JProfiler здесь не очень поможет
Асаф Месика

2

Проблема вне кучи, поэтому лучшим кандидатом являются:

JNI leak  
Allocation of direct memory buffer

Из-за того, что вы ограничили размер прямого буфера, лучшим вариантом, на мой взгляд, является утечка JNI.


1

Существует удобный инструмент для просмотра распределения кучи памяти, включенной в JDK, называемый jmap, помимо этого у вас также есть стек и т. Д. (Xss). Выполните эти две команды jmap, чтобы получить больше информации об использовании памяти:

jmap -heap <PID>
jmap -permstat <PID>

Чтобы получить еще больше информации, вы можете подключиться к процессу с помощью jconsole (также входит в JDK). Однако Jconsole требует, чтобы JMX был настроен в приложении.


Спасибо за ваш ответ, но проблема, кажется, где-то вне кучи. Я обновлю верхний пост, чтобы отразить некоторую информацию из jmap
Константин С.

Сколько потоков у процесса? Размер стека по умолчанию составляет 2 МБ на большинстве платформ, поэтому умножьте его на количество потоков. Я был бы удивлен, если бы он объяснял всю «недостающую» память, но, возможно, какую-то ее часть.
HampusLi

Около 300 потоков, взятых из pmap, у нас размер стека 1Mb. И из того же вывода pmap, похоже, что ни один из этих стеков не использует более 100 Кбайт
Константин С.

0

Используйте JVisualVM. Он имеет различные представления, которые подскажут вам, сколько памяти используется, PermGen и так далее и тому подобное.

Что касается ответа на ваш вопрос. Java обращается с памятью совершенно иначе, чем вы могли бы ожидать.

Когда вы устанавливаете параметры -Xms и -Xmx, вы указываете JVM, сколько памяти должно выделяться в куче для начала, а также, сколько должно выделяться как максимум.

Если у вас есть Java-приложение, которое использовало всего 1 м памяти, но передало -Xms256m -Xmx2g, JVM инициализирует себя с использованием 256 м используемой памяти. Это не будет использовать меньше, чем это. Неважно, что ваше приложение использует только 1 м памяти.

Во- вторых. В приведенном выше случае, если ваше приложение в какой-то момент использует более 256 м памяти, JVM выделит столько памяти, сколько необходимо для обслуживания запроса. Однако это не приведет к уменьшению размера кучи до минимального значения. По крайней мере, не в большинстве случаев.

В вашем случае, поскольку вы устанавливаете минимальную и максимальную память в 2g, JVM выделит 2g в начале и поддержит ее.

Управление памятью Java довольно сложно, и настройка использования памяти может быть само по себе задачей. Однако есть много ресурсов, которые могут помочь.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.