мы пытаемся исследовать использование памяти процессом 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 Мб? Что их использует? Какие данные в них?
Спасибо
Ответы:
Проблема может быть связана с этой проблемой glibc .
По сути, когда у вас несколько потоков, выделяющих память, glibc будет увеличивать количество доступных арен для распределения, чтобы избежать конфликта блокировок. Размер арены составляет 64 МБ. Верхний предел состоит в том, чтобы создать в 8 раз больше ядер ядер. Арены создаются по требованию, когда поток получает доступ к уже заблокированной арене, поэтому со временем он увеличивается.
В Java, где вы поливаете потоками, это может быстро привести к созданию множества арен. И все эти ассигнования распространились по всем этим аренам. Первоначально каждая 64-мегабайтная арена представляет собой просто неиспользуемую память, но когда вы делаете выделения, вы начинаете использовать для них реальную память.
Ваш pmap, вероятно, имеет списки, похожие на это ниже. Обратите внимание, что 324K + 65212K = 65536K, 560K + 64976K == 65536K, 620K + 64916K == 65536K. То есть они составляют до 64Мб.
Что касается обходных путей : в ошибке упоминаются некоторые параметры среды, которые вы можете установить для ограничения количества арен, но вам нужна достаточно высокая версия glibc.
источник
Как насчет Lamdba Probe ? Среди прочего он может показать вам разбивки использования памяти, подобные скриншоту ниже:
Иногда
pmap -x your_java_pid
также может быть полезным.источник
JProfiler может быть чем-то, что вы ищете, но это не бесплатно. Другим хорошим и бесплатным инструментом для исследования использования памяти процессом Java является Java VisualVM, доступный в качестве инструмента JDK в дистрибутивах Oracle / Sun JDK. Я лично рекомендую более целостный подход к проблеме (например, для мониторинга дисков JDK + OS + и т. Д.) - использование некоторой системы сетевого мониторинга - Nagios, Verax NMS или OpenNMS.
источник
Проблема вне кучи, поэтому лучшим кандидатом являются:
Из-за того, что вы ограничили размер прямого буфера, лучшим вариантом, на мой взгляд, является утечка JNI.
источник
Существует удобный инструмент для просмотра распределения кучи памяти, включенной в JDK, называемый jmap, помимо этого у вас также есть стек и т. Д. (Xss). Выполните эти две команды jmap, чтобы получить больше информации об использовании памяти:
Чтобы получить еще больше информации, вы можете подключиться к процессу с помощью jconsole (также входит в JDK). Однако Jconsole требует, чтобы JMX был настроен в приложении.
источник
Используйте JVisualVM. Он имеет различные представления, которые подскажут вам, сколько памяти используется, PermGen и так далее и тому подобное.
Что касается ответа на ваш вопрос. Java обращается с памятью совершенно иначе, чем вы могли бы ожидать.
Когда вы устанавливаете параметры -Xms и -Xmx, вы указываете JVM, сколько памяти должно выделяться в куче для начала, а также, сколько должно выделяться как максимум.
Если у вас есть Java-приложение, которое использовало всего 1 м памяти, но передало -Xms256m -Xmx2g, JVM инициализирует себя с использованием 256 м используемой памяти. Это не будет использовать меньше, чем это. Неважно, что ваше приложение использует только 1 м памяти.
Во- вторых. В приведенном выше случае, если ваше приложение в какой-то момент использует более 256 м памяти, JVM выделит столько памяти, сколько необходимо для обслуживания запроса. Однако это не приведет к уменьшению размера кучи до минимального значения. По крайней мере, не в большинстве случаев.
В вашем случае, поскольку вы устанавливаете минимальную и максимальную память в 2g, JVM выделит 2g в начале и поддержит ее.
Управление памятью Java довольно сложно, и настройка использования памяти может быть само по себе задачей. Однако есть много ресурсов, которые могут помочь.
источник