My test code:
int SIZE = 1900;
int[][] array = new int[SIZE][];
for (int i = 0; i < SIZE; i++) {
array[i] = new int[1024 * 1024 / 4]; // 1MB
Thread.sleep(10);
if (i % 100 == 0 && i != 0) {
System.out.println(i + "Mb added");
}
}
I launch it with arguments in java 8 -Xmx2048m -XX:+UseG1GC -XX:+PrintGCDetails
And it fails with OutOfMemory when only 1G is consumed.
Heap
garbage-first heap total 2097152K, used 1048100K [0x0000000080000000, 0x0000000080104000, 0x0000000100000000)
region size 1024K, 1 young (1024K), 0 survivors (0K)
Metaspace used 3273K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 358K, capacity 388K, committed 512K, reserved 1048576K
I see that G1 allocated size is 2G and I suppose JVM is trying to allocate more and fails with OOM. But why is it trying to allocate more if half of the memory is free?
With UseConcMarkSweepGC
it's working fine and array was fully filled .
I'm pretty sure this happens due to Humongous Allocations.
If you add this option
-XX:+PrintAdaptiveSizePolicy
you will be able to see that most of the allocations are of 1048592 bytes which fits neither 50% nor even 100% of a single G1 region (which as seen in the output is 1024K=1048576 bytes). I assume that means that every array occupies at least two regions. Since it is a humongous allocation most of the free space in the second region cannot be used. This quickly causes extreme heap fragmentation making further allocations impossible.
Agree with @yegodm. Solution is to increase the Heap region with -XX:G1HeapRegionSize to make sure previous Humongous objects are no longer Humongous and will follow the regular allocation path. Read more about humongous object allocation here1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With