Поощрять JVM к GC, а не вырастить кучу?

(Обратите внимание, что когда я говорю "JVM", я действительно имею в виду "Hotspot", и я запускаю последнее обновление для Java 1.6.)

Пример ситуации:

My JVM работает с -Xmx, установленным в 1gb. В настоящее время в куче выделено 500 МБ, из которых используется 450 МБ. Программе необходимо загрузить еще 200 мб в кучу. В настоящее время в куче "сборный" мусор стоит 300 мб (мы будем считать все это в старейшем поколении.)

При нормальной работе JVM вырастет кучу до 700 мб или около того, и мусор будет собираться, когда он приблизится к ней.

Что бы я хотел в этой ситуации, для JVM для gc сначала, а затем выделите новый материал, чтобы в итоге получилось, что размер кучи остался на уровне 500 Мб, а используемая куча на 350 мб.

Есть ли компилятор параметра JVM, который делает это?

Ответ 1

Вы можете попробовать указать -XX:MinHeapFreeRatio и -XX:MaxHeapFreeRatio для управления расширением и сжатием кучи:

  • -XX:MinHeapFreeRatio - когда процент свободного места в поколении падает ниже этого значения, генерирование будет расширено для удовлетворения этого процента. Значение по умолчанию - 40.
  • -XX:MaxHeapFreeRatio - когда процент свободного места в гене превысил это значение, генерация будет уменьшаться, чтобы соответствовать этому значению. По умолчанию 70.

Вы также можете поэкспериментировать с параллельным GC, указав -XX:+UseConcMarkSweepGC. В зависимости от вашего приложения он может уменьшить размер кучи за счет дополнительных циклов процессора.

JVM в противном случае будет использовать указанную вами память как доступную, когда это будет оптимально. Вы можете указать более низкую сумму, например -Xmx768m, чтобы она содержалась, и она может работать нормально, хотя вы увеличили бы риск нехватки памяти в сценарии с большой нагрузкой. На самом деле единственный способ использовать меньше памяти в целом - написать код, который использует меньше памяти:)

Ответ 2

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

  • Серийный коллектор имеет -XX:MinHeapFreeRatio и -XX:MaxHeapFreeRatio, что позволяет вам более или менее напрямую контролировать поведение. Однако у меня нет большого опыта работы с последовательным сборщиком.
  • Параллельный коллектор (-XX:+UseParallelOldGC) имеет -XX:MaxGCPauseMillis=<millis> и -XX:GCTimeRatio=<N>. Установка более низкого максимального времени паузы обычно приводит к тому, что сборщик уменьшает кучу. Однако, если время паузы уже мало, куча не станет меньше. OTOH, если максимальное время паузы установлено слишком низко, приложение может потратить все свое время на сбор. Установка более низкого отношения времени gc также, как правило, делает кучу меньше. Вы сообщаете gc, что вы готовы посвятить больше времени CPU сборке в обмен на меньшую кучу. Однако это всего лишь намек и не может иметь никакого эффекта. На мой взгляд, параллельный коллектор близок к непобедимым с целью минимизации размера кучи. Этот сборщик работает лучше (он настраивается сам), если вы позволите ему работать некоторое время, и поведение приложения остается постоянным.
  • Коллекционер CMS (-XX:+UseConcMarkSweepGC) обычно требует большей кучи, чтобы выполнить свою основную задачу с низким временем паузы, поэтому я не буду обсуждать его.
  • Новый сборщик G1 (-XX:+UseG1GC) не похож на мозг, как старые коллекторы. Я считаю, что он сам выбирает меньший размер кучи. У него много настроек, хотя я только начал их изучать. -XX:InitiatingHeapOccupancyPercent, -XX:G1HeapWastePercent, -XX:G1ReservePercent и -XX:G1MaxNewSizePercent могут представлять интерес.

Взгляните на официальную документацию этот список флагов.

Ответ 3

Вы могли бы как обычно вызывать метод System.gc() перед распределением дополнительных 200 МБ объектов, но это будет просто подсказкой к JVM, который он может или не может следовать, и в любом случае вы не будете знать, когда это произойдет.. как указано внутри документации, это запрос с наилучшими усилиями.

Кстати, учтите, что сбор мусора - это тяжелая операция, поэтому, если нет конкретной необходимости делать это, просто не пытайтесь его принудительно.

Просто для того, чтобы знать: если вы установили -Xmx1G, то вы сообщаете JVM, что 1 ГБ является максимальным местом для кучи, и, поскольку вы указываете это, я не понимаю, почему он должен стараться держать его в покое если он знает, что 1GB будет по-прежнему в порядке (мы находимся в контексте языков, управляемых памятью). Если вы не хотите, чтобы куча увеличилась настолько просто, уменьшите максимальное значение, чтобы он был вынужден выполнять GC перед распределением новых объектов, иначе почему вы говорите ему использовать 1 ГБ?