Java HeapDumps указывает, что используемый размер кучи на 30% меньше фактического определения кучи после исключений OutOfMemory.

У меня есть несколько дампов кучи, которые я анализирую после того, как JVM выдала OutOfMemory исключений. Я использую Hotspot JDK 1.7 (64-разрядная версия) на платформе Windows 2008R2. Сервер приложений представляет собой JBoss 4.2.1GA, запускаемый с помощью Tanuki Java Service Wrapper. .

Он запускается со следующими аргументами:

wrapper.java.additional.2=-XX:MaxPermSize=256m
wrapper.java.initmemory=1498
wrapper.java.maxmemory=3000
wrapper.java.additional.19=-XX:+HeapDumpOnOutOfMemoryError

что переводится как:

-Xms1498m -Xmx3000m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError

Есть и другие параметры конфигурации GC и JMX.

Моя проблема заключается в том, что когда я анализирую дамп кучи, созданный из-за OutOfMemoryException, с помощью Eclipse Memory Analyzer, неизменно, MAT показывает размер кучи 2,3 ГБ или 2,4 ГБ. Я уже включил параметр в MAT на Keep Unreachable Objects, поэтому я не верю, что MAT урезает кучу.

java.lang.RuntimeException: java.lang.OutOfMemoryError: GC overhead limit exceeded

or

java.lang.OutOfMemoryError: Java heap space

Резюме в МАТ:

Size: 2.3 GB Classes: 21.7k Objects: 47.6m Class Loader: 5.2k

Мои фактические размеры файлов кучи составляют примерно 3300 КБ, поэтому они соответствуют моей настройке максимального размера кучи 3000 м.

Так где же недостающие 500-600М памяти в МАТ? Почему MAT показывает размер моей кучи только как 2,4 ГБ?

Другие сообщения на SO, как правило, указывают на то, что JVM выполняет некоторый GC перед сбросом кучи, но если недостающие 500M связаны с GC, почему он вообще выбрасывает OOM? Если GC действительно может очистить 500M (или почти 25% моей кучи), действительно ли JVM не хватает памяти?

Есть ли способы настроить дампы кучи, чтобы я мог получить полную/полную картину кучи (включая недостающие 500 МБ)?

Если нет, то я действительно изо всех сил пытаюсь понять, как/почему я вообще сталкиваюсь с этими OOM.

По чьей-то просьбе я прикрепляю вывод jstat -gc <PID> 1000 из работающего узла: http://pastebin.com/07KMG1tr.


person Eric B.    schedule 06.06.2016    source источник
comment
MAT просто печатает число, которое хранится в заголовке файла дампа кучи. Это число ничего не говорит о полноте графа объектов в файле дампа.   -  person Holger    schedule 07.06.2016


Ответы (2)


Какой ГК вы используете? Возможно, вам не хватает Eden, попробуйте использовать jstat - Java Virtual Инструмент мониторинга статистики компьютеров

person ebanouz    schedule 06.06.2016
comment
Насколько я могу судить, он использует сборщик мусора по умолчанию, которым, насколько мне известно, является ParallelGC. - person Eric B.; 06.06.2016
comment
Не могли бы вы поделиться своим выводом jstat? - person ebanouz; 06.06.2016
comment
Любые конкретные параметры/конфигурации, которые я должен использовать для jstat, которые вы хотели бы видеть? - person Eric B.; 06.06.2016
comment
jstat -gc PID 1000 должно быть достаточно - person ebanouz; 06.06.2016
comment
Очевидно, получить доступ к производственной JVM для запуска jstat не так просто, как я думал, и требуется несколько различных уровней вмешательства. Насколько критична статистика? - person Eric B.; 06.06.2016
comment
Возможно, мне будет проще получить некоторую статистику через плагин VisualGC для VisualVM; позвольте мне посмотреть, что я могу сделать. - person Eric B.; 06.06.2016
comment
Это было бы полезно. А пока включите недоступные объекты в MAT, см.: wiki.eclipse.org/MemoryAnalyzer/ чтобы включить опцию: Настройки-> Анализатор памяти-> Сохранить недоступные объекты и перезагрузить кучу - person ebanouz; 06.06.2016
comment
Я уже включил это (см. начальный пост). И это не имело никакого значения при просмотре кучи. Я даже удалил все индексные файлы на всякий случай и полностью перезагрузил файл hprof, и MAT переиндексировал все это. - person Eric B.; 06.06.2016
comment
В соответствии с просьбой, вот дамп из вывода jstat. pastebin.com/07KMG1tr - person Eric B.; 06.06.2016
comment
спасибо, я не знаю, что делает ваше приложение, но из jstat я вижу, что в течение этих 303 секунд не было никаких крупных gc. мое лучшее предложение: 1. использовать jmap для анализа объема памяти jmap -histo ‹pid› - person ebanouz; 07.06.2016
comment
2. Используйте Concurrent Mark and Sweep GC: -XX:ConcMarkSweepGC 3. попробуйте изменить размер поколений: Sizing the Generations- docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ - person ebanouz; 07.06.2016

java.lang.OutOfMemoryError: превышен лимит накладных расходов GC

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

java.lang.OutOfMemoryError: пространство кучи Java

и это не означает, что в вашей куче осталось 0 байтов, это означает, что запрос на выделение не может быть удовлетворен. Если что-то попытается выделить 600 МБ, а останется только 500 МБ, это вызовет OOME.

Если нет, то я действительно изо всех сил пытаюсь понять, как/почему я вообще сталкиваюсь с этими OOM.

Для начала было бы получение трассировки стека, чтобы увидеть, делает ли сайт вызова, выполняющий рассматриваемое распределение, что-либо подозрительное. Или вы можете просто попробовать увеличить размер кучи и посмотреть, исчезнет ли проблема.

person the8472    schedule 06.06.2016
comment
Я понимаю, что GC overhead limit exceeded означает не то, что он заполнен, а то, что он не может освободить достаточно места. Однако кажется странным, что он выдаст такое исключение, если не need освободит дополнительное пространство. И я не могу представить, как в java можно выделить блок памяти размером 500 МБ за один вызов. Возможно, с начальным выделением массива или хеш-таблицы/и т. д. с огромным размером, но кроме этого я не могу представить ничего, что могло бы выделить такой размер куска памяти. Я упускаю что-то очевидное? - person Eric B.; 06.06.2016