Достижение точки насыщения при загрузке данных; как я могу контролировать поколения Java GC?

Я использую JVM для научного приложения. Первым шагом в моем процессе является загрузка большого количества данных в маленькие массивы double[] (массивы из 48 элементов для каждого узла в большом графе). Задолго до того, как я узнаю, достаточно ли у меня памяти, чтобы загрузить их все, Java асимптотически замедляется, и jvisualvm говорит мне, что это связано с тем, что почти все время процессора тратится на сборку мусора:

введите здесь описание изображения

Первая минута или около того в порядке: «использованная куча» (правый график) прыгает вверх и вниз по мере роста, потому что некоторые объекты являются временными (я написал это на Scala), а некоторые объекты являются постоянными. Однако после этого загрузка данных останавливается, потому что сборщик мусора, по-видимому, снова и снова проверяет одни и те же объекты (левый график). Должно быть, ожидается, что они выйдут за рамки, но я оставляю их в рамках, потому что хочу использовать их для своего анализа.

Я знаю, что сборщик мусора помещает объекты в разные поколения, исходя из их вероятности выживания. Первое поколение содержит недавно созданные объекты, которые, вероятно, скоро исчезнут; более поздние поколения имеют все больше шансов быть долгожителями. Если мои объекты ошибочно относятся к первому поколению, есть ли способ сообщить сборщику мусора, что они должны принадлежать к более позднему поколению? Я знаю, что сохраню их --- как мне сказать об этом сборщику мусора?

Хотя я бы хотел, чтобы эти объекты были в более постоянном поколении, PermGen был бы слишком далеко: в конце концов они умрут после десятков минут обработки. (Я хочу использовать это в редукторе Hadoop, который может работать с другим фрагментом данных после этого без новой JVM.)

Примечание. Я использую виртуальную машину Sun HotSpot:

% java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

Исправление (к предыдущему редактированию): изменение -Xmx действительно изменяет точку насыщения, но очевидно, что Java игнорирует аргумент командной строки -Xmx, если он передается после аргумента -jar. То есть сделать

java -Xmx2048 -jar MyJarFile.jar

скорее, чем

java -jar MyJarFile.jar -Xmx2048

Из-за этого я неправильно диагностировал поведение в отношении максимальной кучи, и все ответы, указывающие на флаг -Xmx, действительны.

Точка насыщения, которую я описываю, происходит, когда «размер кучи» (оранжевый на правом графике) достигает выбранного предела -Xmx, а «размер кучи» всегда примерно в 1,6 раза больше «используемой кучи» (синий на правом графике), если вы явно не установили размер «Старого» поколения с -XX:NewRatio или -XX:OldSize. Они также должны быть перед аргументом -jar, и они обеспечивают большой контроль.


person Jim Pivarski    schedule 25.09.2013    source источник
comment
Вы не можете повторно использовать объекты в любом случае?   -  person exussum    schedule 25.09.2013
comment
Повторно использовать объекты? Что ты имеешь в виду? Я загружаю их в память, чтобы выполнить анализ всех из них. Они представляют разные данные.   -  person Jim Pivarski    schedule 25.09.2013
comment
они могут представлять разные данные, но, скорее всего, это один и тот же объект. a = 12 и b = 1534545 различны, но если я не использую их одновременно, я мог бы повторно использовать a вместо создания нового b и вызова gc для a   -  person exussum    schedule 25.09.2013
comment
Я намерен использовать все эти объекты одновременно (чтобы кластеризация k-средних в разделах определялась из структуры графа), поэтому все они должны быть загружены в память одновременно.   -  person Jim Pivarski    schedule 25.09.2013


Ответы (3)


Я думаю, вам следует проверить это с помощью подключаемого модуля VisualGC JVisualVM, поэтому чтобы вы могли видеть, как используются разные поколения. Судя по скринам, кажется, что старая генерация забита (поскольку куча не до конца заполнена, но сборщик мусора работает в полную силу), поэтому сборщик мусора с трудом освобождает память. Вы должны либо увеличить кучу, либо настроить размер поколений с помощью -XX:NewRatio, и вы можете попробовать настроить порог владения а также контролировать, когда объект считается «старым».

person Katona    schedule 25.09.2013
comment
Это на самом деле то, что происходило: пространство кучи не было заполнено, но старое поколение было. (GC правильно помечал мои данные как старые, но старые были заполнены. Тогда я не понимаю, почему GC усердно работал, а не терпел неудачу.) Плагин VisualGC был очень полезен для диагностики ситуации, а -XX:NewRatio и его родственники (например, -XX:OldSize) особенно полезны для настройки GC для больших вычислений в памяти. Спасибо! - person Jim Pivarski; 25.09.2013

Сборщик мусора не должен вызывать себя по спирали, если ваша куча не приближается к состоянию насыщения. Вам нужно увеличить максимальный размер кучи (-Xmx) — начните с чего-то, что приближается к 2-кратному ожидаемому удержанию. Вы также можете использовать сборщик CMS, который может улучшить ситуацию с большим штатным набором. Вам также, вероятно, придется настраивать новое поколение вручную, так как старое поколение не нужно будет регулярно проверять.

Вы также можете рассмотреть возможность использования NIO напрямую ByteBuffer.html">ByteBuffers. Хотя они предназначены для более эффективных операций ввода-вывода, они могут быть разумным выбором для очень долгоживущих и больших массивов памяти.

person Yann Ramin    schedule 25.09.2013

Объекты не удаляются сборщиком мусора, если на них все еще есть ссылки. Поэтому просто сохраняйте ссылки на объекты, пока не захотите, чтобы они были удалены сборщиком мусора.

person jhocking    schedule 25.09.2013