这个SO answer阐明了-Xmx
JVM标志的一些内容。试图尝试我做了以下事情:
import java.util.List;
import java.util.ArrayList;
public class FooMain {
private static String memoryMsg() {
return String.format("%s. %s. %s"
, String.format("total memory is: [%d]",Runtime.getRuntime().totalMemory())
, String.format("free memory is: [%d]",Runtime.getRuntime().freeMemory())
, String.format("max memory is: [%d]",Runtime.getRuntime().maxMemory()));
}
public static void main(String args[]) {
String msg = null;
try {
System.out.println(memoryMsg());
List<Object> xs = new ArrayList<>();
int i = 0 ;
while (true) {
xs.add(new byte[1000]);
msg = String.format("%d 1k arrays added.\n%s.\n"
, ++i
, memoryMsg());
}
} finally {
System.out.printf(msg);
}
}
}
使用javac FooMain.java
进行编译。当我运行它的最大堆大小为500万字节时,我得到:
java -Xmx5000000 FooMain
total memory is: [5242880]. free memory is: [4901096]. max memory is: [5767168]
4878 1k arrays added.
total memory is: [5767168]. free memory is: [543288]. max memory is: [5767168].
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.String.toCharArray(String.java:2748)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:3048)
at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2744)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:2702)
at java.util.Formatter.format(Formatter.java:2488)
at java.util.Formatter.format(Formatter.java:2423)
at java.lang.String.format(String.java:2792)
at FooMain.memoryMsg(FooMain.java:7)
at FooMain.main(FooMain.java:21)
虽然数字足够接近,但它们看起来并不十分准确(最后total memory
除了正好 max memory
)。特别是5368个1000字节的数组每个应该占用超过5000000甚至5242880字节。这些数字应该如何理解?
这是我正在使用的java:
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) Server VM (build 24.80-b11, mixed mode)
答案 0 :(得分:6)
查看OpenJDK源代码,确定最大堆大小的逻辑非常复杂,并且由很多变量决定。实际堆大小在hotspot/src/share/vm/memory/collectorPolicy.cpp
中设置,它使用提供的-Xmx值作为输入,并使用以下代码对齐它:
align_size_up(MaxHeapSize, max_alignment());
align_size_up定义为:
#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))
max_alignment是虚拟内存页面大小和JVM垃圾收集卡大小的乘积。 VM页面大小为4096字节,卡大小为512字节。插入这些值会给出实际的MaxHeapSize为6324224字节,这与您看到的数字很相符。
注意:我只是简单地查看了JVM代码,因此我可能错过了一些内容,但答案似乎与您所看到的相符。
答案 1 :(得分:5)
是-Xmx的硬限制吗?
这取决于你所说的“硬”。如果你的意思是“不能被程序改变”那么是。如果你的意思是“精确”,那么否。垃圾收集器使用的各种空间的大小是2字节的一些幂的倍数。显然,JVM向上而不是向下。
分配1000字节数组的示例程序
如何理解这些数字?
1000字节的Java数组不会精确占用1000个字节:
有一个对象标题,包括32位length
字段的空格。 (通常总共3 x 32位)。
堆节点以2 ^ N个字节的倍数分配。 (通常为2 ^ 3 == 8)
然后你有了导致OutOfMemoryError
的原因的问题。您可能认为这是因为堆已满。但是,在这种情况下,消息显示“超出GC开销限制”。这意味着JVM检测到JVM在运行垃圾收集器的整个CPU时间中所占的比例过大。这就是杀死GC的原因......没有内存耗尽。
“GC开销限制”的基本原理是当堆完全关闭时,GC会频繁运行,并且每次都会设法回收越来越少的内存。当你进入GC“死亡螺旋”时,最好快速拔出插头,而不是让应用程序磨到最终的失败点。
无论如何......这意味着你的启发式算法可以解决堆满了时分配多少内存的问题。
答案 2 :(得分:1)
到目前为止,其他人已经回答了你的问题,但出于兴趣,我已经完成了数学运算: - )
陈述here:
此值必须是1024的倍数,大于2 MB。
5000000不是1024的倍数。我的猜测是该参数被四舍五入为1024的倍数,其他答案确认。您的总内存(不仅仅是声明的堆空间)是5767168,即5632 * 1024
。它在运行时从最初的5242880扩大到适合不断增长的堆空间。请注意-Xmx
声明了最大值,因此不一定要立即分配。在this source的帮助下,我们可以使您的内存使用量接近(假设为64位):
4878000
个字节4878 * 24 = 117072
字节数组开销所以数组占用了4995072个字节(巧合的是?)已经是1024的倍数。但是其他对象仍然存在开销。所以堆空间是1024的大于4995072且低于5767168的倍数。考虑到最后的可用空间,这为剩下的堆和非堆内存使用留下了228808个字节,这听起来像是一个合理的数字。
最后还有一个关于自由空间的文字。在崩溃之前,JVM不一定会填满所有内存。如果内存即将耗尽,则垃圾收集会更频繁地运行。如果这占用整个运行时的一定百分比,the JVM exits,就在您的情况下发生。