I've got short-living applications which usually (but not always) do not need any GC (fits in heap, epsilon GC proves this by not causing an OOM).
Interestingly, G1 still kicks in very early even though there's still plenty of heap free:
[0.868s][info ][gc,start ] GC(0) Pause Young (Normal) (G1 Evacuation Pause)
[0.869s][info ][gc,task ] GC(0) Using 13 workers of 13 for evacuation
[0.872s][info ][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms
[0.873s][info ][gc,phases ] GC(0) Evacuate Collection Set: 2.8ms
[0.873s][info ][gc,phases ] GC(0) Post Evacuate Collection Set: 0.4ms
[0.873s][info ][gc,phases ] GC(0) Other: 1.0ms
[0.873s][info ][gc,heap ] GC(0) Eden regions: 51->0(45)
[0.873s][info ][gc,heap ] GC(0) Survivor regions: 0->7(7)
[0.873s][info ][gc,heap ] GC(0) Old regions: 0->2
[0.873s][info ][gc,heap ] GC(0) Humongous regions: 4->2
[0.873s][info ][gc,metaspace ] GC(0) Metaspace: 15608K->15608K(1062912K)
[0.874s][info ][gc ] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 55M->10M(1024M) 5.582ms
[0.874s][info ][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.01s
[...]
It makes me wonder why GC runs here at all as heap is only 55MB.
In total I have usually 10-15 GC runs which aggregate to a consumed user cpu time of ~1 second which I'd like to avoid.
JVM: openjdk version "11.0.16" 2022-07-19
JVM ARGS: -Xms1g -Xmx2g -XX:+PrintGCDetails -Xlog:gc+cpu=info -Xlog:gc+heap+exit
Question:
How can I tune G1 (jdk 11) to kick in as late as possible (e.g. when heap/eden is 90% full) to ideally avoid any GC pauses/runs in most of my cases?
Increasing -XX:InitiatingHeapOccupancyPercent
(e.g. to 90%) did not help in my case.
EDIT:
Try it out by yourself by executing this java class on your jvm:
public class GCTest {
public static void main(String[] args) {
java.util.Map<String,byte[]> map = new java.util.HashMap<>();
for(int i=0;i<1_000_000;i++)
map.put(i+"", new byte[i % 256]);
System.out.println(map.size());
}
}
This application consumes about 260MB heap and runs less than 500ms.
When started with the following jvm arguments:
-Xms1g -Xmx2g -XX:+PrintGCDetails -Xlog:gc+cpu=info -Xlog:gc+heap+exit
you will get ~5-6 GC runs (tested with java 11+16 hotspot vm) .
GC Epsilon tests clearly shows that it can run without any GCing.
Challenge:
Can you find jvm arguments which will force G1 to not do any GCing here?
native-image
/AOT compilation. Aside from that, you might want to use-client
. – Highclass-XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=45
or similar. – Highclass-XX:G1NewSizePercent
as well but this did not help either. – Creolacreole-Xmn
or decrease the size of survivor spaces. I checked that the following arguments work:-XX:+UseParallelGC -Xmx500m -Xmn300m -XX:SurvivorRatio=16
– Verde-Xmx2g -Xmn1g
, it works with the ParallelGC. However, when increasing the iteration count from1_000_000
to3_000_000
(which still can run with an epsilon GC) I was unable to find settings for ParallelGC to avoid any GC runs while G1 will surprisingly still not run. Do you have an idea? – Creolacreolejava -Xlog:gc -XX:+UseParallelGC -Xmx2g -Xms2g -Xmn1g GCTest
– Verde[0.800s][info][gc] GC(0) Pause Young (Allocation Failure) 768M->525M(1920M) 149.919ms
(3M iterations) on Windows. Just tried it on Linux and there is indeed no GC when using the same/your settings - interesting! – Creolacreole-XX:+UseCompressedOops
or-XX:+CompactStrings
is off. Otherwise the total allocated memory should not have reached 768M. – Verde