Статьи

Изменение размера кучи во время выполнения


Новый день, новая версия Java … приятно 🙂

Мы использовали для обновления второстепенных версий Java в основном из-за проблем безопасности. Но иногда эти незначительные обновления приносят нам совершенно новые, потрясающие функции. Так было, например, с 7u40, который принес нам потрясающий контроль полетов, известный из JRockit. (Если вы заинтересованы в отслеживании изменений в обновлениях Java, ознакомьтесь с

http://www.oracle.com/technetwork/java/javase/8all-relnotes-2226344.html  для Java 8 или
http://www.java. com / en / download / faq / release_changes.xml  для Java 7) 

Та же ситуация сейчас с версиями 
7u60  и 
8u20 . Теперь флаги 
MinHeapFreeRatio  и
MaxHeapFreeRatio стало управляемым, что означает, что мы можем изменить его значения … во время выполнения 🙂 

Вы всегда можете проверить, какими флагами в JVM можно управлять, вызвав

java -XX:+PrintFlagsFinal -XX:+UseG1GC -version |grep manageable
intx CMSAbortablePrecleanWaitMillis  = 100  {manageable}
intx CMSWaitDuration  = 2000  {manageable}
bool HeapDumpAfterFullGC  = false  {manageable}
bool HeapDumpBeforeFullGC  = false  {manageable}
bool HeapDumpOnOutOfMemoryError  = false  {manageable}
ccstr HeapDumpPath  =  {manageable}
uintx MaxHeapFreeRatio  = 70  {manageable}
uintx MinHeapFreeRatio  = 40  {manageable}
bool PrintClassHistogram  = false  {manageable}
bool PrintClassHistogramAfterFullGC  = false  {manageable}
bool PrintClassHistogramBeforeFullGC  = false  {manageable}
bool PrintConcurrentLocks  = false  {manageable}
bool PrintGC  = false  {manageable}
bool PrintGCDateStamps  = false  {manageable}
bool PrintGCDetails  = false  {manageable}
bool PrintGCTimeStamps  = false  {manageable}
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)

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

Теперь посмотрим, как работает изменение размера кучи. Сначала нам нужно найти Java-процесс для тестирования. Я собираюсь использовать запуск 
IntelliJ IDEA .

Как вы знаете, java pids можно найти, вызвав 
 команду
jps :

jps -l
17136 com.intellij.idea.Main

Мы уже знаем наш pid для тестирования, поэтому самое время узнать статистику кучи. Мы будем использовать 
команду
jmap для этого:

jmap -heap 17136
Attaching to process ID 17136, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.20-b23
using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio  = 40
MaxHeapFreeRatio  = 70
MaxHeapSize  = 2147483648 (2048.0MB)
NewSize  = 1363144 (1.2999954223632812MB)
MaxNewSize  = 1287651328 (1228.0MB)
OldSize  = 5452592 (5.1999969482421875MB)
NewRatio  = 2
SurvivorRatio  = 8
MetaspaceSize  = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize  = 17592186044415 MB
G1HeapRegionSize  = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions  = 2048
capacity = 702545920 (670.0MB)
used  = 284513824 (271.3335266113281MB)
free  = 418032096 (398.6664733886719MB)
40.49754128527285% used

Как видите, я использую 
G1 GC  с максимальным размером кучи 2 ГБ (-Xmx2g). Использование кучи составляет около 40%. Теперь мы должны запустить full gc, чтобы немного очиститься перед игрой.

jcmd 17136 GC.run

Тогда jmap еще раз, и мы можем увидеть 

Heap Configuration:
MinHeapFreeRatio  = 40
MaxHeapFreeRatio  = 70
Heap Usage:
G1 Heap:
regions  = 2048
capacity = 439353344 (419.0MB)
used  = 135954032 (129.65586853027344MB)
free  = 303399312 (289.34413146972656MB)
30.944121367607025% used

Наконец-то мы готовы начать сбор. Я изменю значение 
 флага
MaxHeapFreeRatio с помощью 
инструмента
jinfo :

jinfo -flag MaxHeapFreeRatio=50 17136

После этого мы должны снова запустить full gc для принудительного изменения размера. И теперь наша куча выглядит так: 

Heap Configuration:
MinHeapFreeRatio  = 40
MaxHeapFreeRatio  = 50
Heap Usage:
G1 Heap:
regions  = 2048
capacity = 166723584 (159.0MB)
used  = 82849400 (79.01134490966797MB)
free  = 83874184 (79.98865509033203MB)
49.69266975450816% used

Как видите, куча сократилась с 419 до 159 МБ.

Теперь мы можем попробовать пойти другим путем: 

jinfo -flag MaxHeapFreeRatio=90 17136
jinfo -flag MinHeapFreeRatio=85 17136

дает 
 вывод 
Jmap

Heap Configuration:
MinHeapFreeRatio  = 85
MaxHeapFreeRatio  = 90
Heap Usage:
G1 Heap:
regions  = 2048
capacity = 465567744 (444.0MB)
used  = 69816504 (66.58220672607422MB)
free  = 395751240 (377.4177932739258MB)
14.995992505872572% used

Изменение размера сработало еще раз, а объем кучи увеличился с 159 МБ до 444 МБ. Мы описали, что минимум 85% нашей емкости кучи должны быть свободными, и это указало JVM изменить размер кучи, чтобы получить максимально 15% использования. Теперь мы ждем смены среды выполнения XMX;)