Поощрять JVM к GC, а не наращивать кучу?
(Обратите внимание, что когда я говорю "JVM", я действительно имею в виду "горячую точку" и запускаю последнее обновление Java 1.6.)
Пример ситуации:
Моя JVM работает с -Xmx, установленным на 1 ГБ. В настоящее время в куче выделено 500 МБ, из которых 450 МБ используется. Программа должна загрузить еще 200 мб в кучу. В настоящее время в куче "собираемого" мусора стоит 300 Мб (предположим, что это все в старом поколении).
При нормальной работе JVM увеличит кучу до 700 Мб или около того и будет собирать мусор, когда доберется до нее.
В этой ситуации я хотел бы, чтобы JVM сначала собрала gc, а затем распределила новый материал, чтобы в итоге мы получили размер кучи, равный 500 МБ, и использованную кучу, равную 350 МБ.
Есть ли параметр JVM, который делает это?
4 ответа
Вы можете попробовать указать -XX:MinHeapFreeRatio
а также -XX:MaxHeapFreeRatio
контролировать расширение и сжатие кучи:
-XX:MinHeapFreeRatio
- когда процент свободного пространства в поколении падает ниже этого значения, поколение будет расширено, чтобы соответствовать этому проценту. По умолчанию 40.-XX:MaxHeapFreeRatio
- когда процент свободного пространства в поколении превысил это значение, поколение будет сокращаться, чтобы соответствовать этому значению. По умолчанию 70.
Вы также можете поэкспериментировать с одновременным сбором мусора, указав -XX:+UseConcMarkSweepGC
, В зависимости от вашего приложения размер кучи может быть меньше за счет дополнительных циклов ЦП.
В противном случае JVM будет использовать указанную вами память как доступную, когда это оптимально. Вы можете указать меньшее количество, как -Xmx768m
чтобы он содержался, и он мог нормально работать, хотя вы бы увеличили риск нехватки памяти в сценарии с большой нагрузкой. На самом деле единственный способ использовать меньше памяти - написать код, который использует меньше памяти:)
В HotSpot есть четыре (на самом деле, пять) разных сборщика мусора, и ваши варианты для каждого из них разные.
- Серийный коллектор имеет
-XX:MinHeapFreeRatio
а также-XX:MaxHeapFreeRatio
, которые позволяют вам более или менее непосредственно контролировать поведение. У меня нет большого опыта работы с серийным коллектором. - Параллельный коллектор (
-XX:+UseParallelOldGC
) имеет-XX:MaxGCPauseMillis=<millis>
а также-XX:GCTimeRatio=<N>
, Установка более низкого максимального времени паузы обычно приводит к уменьшению размера кучи. Однако, если время паузы уже мало, куча не станет меньше. OTOH, если максимальное время паузы установлено слишком низким, приложение может тратить все свое время на сбор. Установка более низкого отношения времени gc также обычно делает кучу меньше. Вы говорите сборщику, что готовы выделить больше процессорного времени для сбора в обмен на меньшую кучу. Тем не менее, это всего лишь подсказка и может не иметь никакого эффекта. На мой взгляд, параллельный коллектор практически не работает для минимизации размера кучи. Этот сборщик работает лучше (он настраивается сам), если вы дадите ему некоторое время поработать, а поведение приложения останется неизменным. - Коллектор CMS (
-XX:+UseConcMarkSweepGC
) обычно требуется большая куча, чтобы выполнить свою главную цель - малое время паузы, поэтому я не буду это обсуждать. - Новый коллектор G1 (
-XX:+UseG1GC
) не такой умопомрачительный, как старые коллекционеры. Я считаю, что он сам выбирает меньший размер кучи. У него много вариантов настройки, хотя я только начал их изучать.-XX:InitiatingHeapOccupancyPercent
,-XX:G1HeapWastePercent
,-XX:G1ReservePercent
а также-XX:G1MaxNewSizePercent
может представлять интерес
Посмотрите официальную документацию для сборщиков мусора, а также этот список флагов.
Вы могли бы, как обычно, ссылаться на System.gc()
метод перед выделением дополнительных 200 МБ объектов, но это будет просто подсказкой для JVM, что он может или не может следовать, и в любом случае вы не будете знать, когда это произойдет... как указано в документации, это лучшее усилие запрос.
Кстати, учтите, что сборка мусора - сложная операция, поэтому, если в этом нет особой необходимости, просто не пытайтесь ее форсировать.
Просто чтобы знать: если вы установите -Xmx1G
затем вы сообщаете JVM, что 1 ГБ является максимальным пространством для кучи, и, поскольку вы указываете это, я не понимаю, почему он должен пытаться поддерживать его на низком уровне, если он знает, что 1 ГБ все еще будет в порядке (мы в контексте языков с управлением памятью). Если вы не хотите, чтобы куча так сильно увеличивалась, просто уменьшите максимальное значение, чтобы он был вынужден выполнить GC перед выделением новых объектов, в противном случае, почему вы говорите ему использовать 1 ГБ?
Я бы рекомендовал заглянуть сюда: