Runtime commited heap resizing

New day, new version of Java... nice :)

We used to update minor Java versions mostly due to security issues. But sometimes those minor updates brings us brand new, awesome features. So was for example with 7u40 which brought us awesome Mission Control known from JRockit. (If you are interested in tracking changes across Java updates take a look at for Java 8 or for Java 7)

Same situation is now with versions 7u60 and 8u20. Since now flags MinHeapFreeRatio and MaxHeapFreeRatio became manageable, which means we can change its values... in runtime :)

You can always check which flags in JVM are manageable by invoking
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)
Getting back to our flags. MinHeapFreeRatio defines minimum percentage of heap free after GC to avoid expansion, while MaxHeapFreeRation (as you can expect) describes maximum percentage of heap free after GC to avoid shrinking.

Now let's see how this heap resizing works. At first we need to find java process right for testing. I'll going to use running IntelliJ IDEA.
As you know java pids can be found by invoking jps command:
jps -l
17136 com.intellij.idea.Main
We already know our testing pid, so it's good time to find out some heap statistics. We'll use jmap command to do that:
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
As you can see I'm using G1 GC with 2GB max heap size (-Xmx2g). Heap usage is about 40%. Now we should run full gc to clean up a little bit before playing.
jcmd 17136
Then jmap one more time and we can see
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
Finally we're ready to start resezing. I will change value of MaxHeapFreeRatio flag by using jinfo tool:
jinfo -flag MaxHeapFreeRatio=50 17136
After that we have to again run full gc to force resizing. And now our heap looks like that:
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
As you can see heap shrank from 419 to 159 MB.

Now we can try going in other way:
jinfo -flag MaxHeapFreeRatio=90 17136
jinfo -flag MinHeapFreeRatio=85 17136
gives jmap output
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
Resizing worked one more time and heap capacity has increased from 159MB to 444MB. We described that minimum 85% of our heap capacity should be free, and that pointed JVM to resize the heap to gain at most 15% usage. Now we're waiting for runtime XMX changing ;)


Popular posts from this blog

Using jstat to report custom JVM metric sets

Understanding Spring Web Initialization

Including Java agent in standalone Spring Boot application