Принудительная полная сборка мусора, когда заполнение памяти выходит за определенные пределы - PullRequest
10 голосов
/ 15 марта 2010

У меня есть серверное приложение, которое в редких случаях может выделять большие куски памяти.

Это не утечка памяти, поскольку эти блоки могут быть возвращены сборщиком мусора путем выполнения полной сборки мусора . Обычный сборщик мусора освобождает слишком малые объемы памяти: в этом контексте этого недостаточно.

Сборщик мусора выполняет эти полные сборочные файлы, когда сочтет это целесообразным, а именно, когда объем памяти приложения приближается к выделенному максимуму, указанному с -Xmx.

Это было бы хорошо, если бы не тот факт, что эти проблемные выделения памяти бывают пакетными , и могут вызывать OutOfMemoryErrors из-за того, что jvm не может выполнить GC достаточно быстро, чтобы освободить необходимую память . Если я заранее вызываю System.gc (), я могу предотвратить эту ситуацию.

В любом случае, я бы предпочел не следить за распределением памяти моего jvm самостоятельно (или вставлять управление памятью в логику моего приложения); было бы неплохо, если бы был способ запустить виртуальную машину с порогом памяти, при котором полные сборочные файлы будут выполняться автоматически, чтобы освободить очень раннюю память, которая мне понадобится.

Короче говоря: мне нужен способ (опция командной строки?) Для настройки jvm для раннего освобождения достаточного количества памяти (т. Е. Выполнения полного GC), когда заполнение памяти достигает определенного порога, я не не волнует, замедляет ли это мое приложение время от времени.

Все, что я нашел до сих пор, - это способы изменить размер поколений, но это не то, что мне нужно (по крайней мере, не напрямую).

Буду признателен за ваши предложения,

Silvio

P.S. Я работаю над тем, чтобы избежать больших выделений, но это может занять много времени, а между тем моему приложению нужна небольшая стабильность

ОБНОВЛЕНИЕ : анализируя приложение с помощью jvisualvm, я вижу, что проблема в старом поколении

Ответы [ 7 ]

7 голосов
/ 15 марта 2010

С здесь (это страница 1.4.2, но такая же опция должна существовать во всех виртуальных машинах Sun):

при условии, что вы используете сборщик мусора CMS (который, по моему мнению, сервер включается по умолчанию), вам нужна опция

-XX:CMSInitiatingOccupancyFraction=<percent>

где% - это% используемой памяти, который запускает полный сборщик мусора.

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

4 голосов
/ 15 марта 2010

Когда вы распределяете крупные объекты, которые не вписываются в молодое поколение, они сразу же распределяются в пространстве постоянного поколения. Это пространство доступно только для GC, когда запускается полный GC, который вы пытаетесь форсировать.

Однако я не уверен, что это решит вашу проблему. Вы говорите: «JVM не может выполнить GC достаточно быстро». Даже если ваши распределения происходят пакетами, каждое выделение заставит виртуальную машину проверить, достаточно ли у нее места для этого. Если нет - и если объект слишком велик для молодого поколения - это вызовет полный сборщик мусора, который должен «остановить мир», предотвращая тем самым новые распределения в первую очередь. Как только GC будет завершен, ваш новый объект будет выделен.

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

Вы говорите: «Мне нужен способ [...] быстро освободить достаточное количество памяти (т.е. выполнить полный сборщик мусора), когда заполнение памяти достигает определенного порога»). Это по определению может быть успешным только в том случае, если на это «хорошее количество памяти» больше ничего не ссылается в вашем приложении.

Из того, что я понимаю здесь, у вас может быть состояние гонки, которого вы можете иногда избегать, перемежая запросы GC вручную. В общем, вам никогда не придется беспокоиться об этих вещах - по моему опыту, OutOfMemoryError возникает только в том случае, если на самом деле слишком много выделений для одновременного размещения в куче. Во всех других ситуациях «единственной» проблемой должно быть снижение производительности (которое может стать экстремальным, в зависимости от обстоятельств, но это другая проблема).

Я предлагаю вам сделать дальнейший анализ точной проблемы, чтобы исключить это. Я рекомендую инструмент VisualVM, который поставляется с Java 6. Запустите его и установите плагин VisualGC. Это позволит вам увидеть разные поколения памяти и их размеры. Также есть множество вариантов ведения журнала, связанных с GC, в зависимости от того, какую виртуальную машину вы используете. Некоторые варианты были упомянуты в других ответах.

Другие варианты выбора GC для использования и настройки пороговых значений не должны иметь значения в вашем случае, поскольку все они зависят от наличия достаточного объема памяти для размещения всех объектов, необходимых вашему приложению в любой момент времени. Эти параметры могут быть полезны, если у вас есть проблемы с производительностью, связанные с большой активностью GC, но я боюсь, что они не приведут к решению в вашем конкретном случае.

Как только вы будете более уверены в том, что на самом деле происходит, поиск решения станет легче.

1 голос
/ 15 марта 2010

JVM должна выдавать OutOfMemoryError только после того, как она попыталась освободить память посредством сборки мусора (согласно документам API для OutOfMemoryError и Спецификация JVM ). Поэтому ваши попытки форсировать сборку мусора не должны иметь никакого значения. Так что здесь может произойти что-то более существенное - либо проблема с вашей программой, неправильно очищающей ссылки, либо, менее вероятно, ошибка JVM.

1 голос
/ 15 марта 2010

Существует очень подробное объяснение того, как работает GC здесь , и в нем перечислены параметры для управления памятью, доступной различным пулам / поколениям памяти.

1 голос
/ 15 марта 2010

Знаете ли вы, какой из пулов сбора мусора становится слишком большим? .... т.е. рай против выживших? (попробуйте опцию JVM -Xloggc:<file> log GC status to a file with time stamps) ... Когда вы это знаете, вы сможете настроить размер созданного пула с помощью одной из упомянутых здесь опций: опции горячей точки для Java 1.4

Я знаю, что эта страница предназначена для 1.4 JVM, и я не могу найти те же опции -X в моих текущих опциях справки по установке 1.6, если только установка этих отдельных размеров пула не является нестандартной, нестандартной функцией!

0 голосов
/ 15 марта 2010

Вы пробовали играть с G1 gc? Он должен быть доступен в версии 1.6.0u14 и далее.

0 голосов
/ 15 марта 2010

Попробуйте использовать параметр -server. Он включит параллельный gc, и у вас будет некоторое увеличение производительности, если вы используете многоядерный процессор.

...