我的测试代码:
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");
}
}我使用java 8 -Xmx2048m -XX:+UseG1GC -XX:+PrintGCDetails中的参数启动它。
当只消耗1G时,它就会在OutOfMemory上失败。
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我看到G1分配的大小是2G,我认为JVM正在尝试分配更多的OOM,但是在OOM中失败了。但是,如果一半的内存是空闲的,它为什么要分配更多的内存呢?
有了UseConcMarkSweepGC,它工作得很好,数组也完全填满了。
发布于 2017-08-29 15:11:29
我很确定这是大额拨款造成的。
如果添加此选项
-XX:+PrintAdaptiveSizePolicy
您将可以看到,大多数分配都是1048592字节,这既不适合单个G1区域的50%,甚至也不适合100% (在输出中看到的是1024K=1048576字节)。我认为这意味着每个数组至少占据两个区域。由于这是一个庞大的分配,第二个区域的大部分自由空间不能使用。这很快会导致极端的堆碎片,使进一步的分配无法进行。
发布于 2017-08-30 12:14:54
同意@yegodm。解决方案是使用-XX:G1HeapRegionSize增加堆区域,以确保以前的大型对象不再庞大,并将遵循常规的分配路径。在这里阅读更多关于大型对象分配的信息(1)
https://stackoverflow.com/questions/45939678
复制相似问题