-Xmx 是硬性限制吗?
is -Xmx a hard limit?
此 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)
查看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 字节。插入这些值给出 6324224 字节的实际 MaxHeapSize,这将与您看到的数字很好地对应。
注意:我只是简单地查看了 JVM 代码,所以我可能遗漏了一些东西,但答案似乎与您所看到的相加。
is -Xmx a hard limit?
这取决于你所说的“困难”是什么意思。如果你的意思是“不能被程序改变”那么是的。如果您的意思是“精确”,那么不是。垃圾收集器使用的各种 space 的大小是字节 2 的某个幂的倍数。显然,JVM 向上舍入而不是向下舍入。
Example program which allocates 1000 byte arrays
How should these numbers be understood?
一个 1000 字节的 Java 数组并不正好占用 1000 字节:
有一个对象头,包括 space 32 位 length
字段。 (通常总共 3 x 32 位)。
堆节点以 2^N 字节的倍数分配。 (通常为 2^3 == 8)
然后你会问是什么导致了 OutOfMemoryError
。您可能认为这是因为堆已满。但是,在这种情况下,消息显示“超出 GC 开销限制”。这意味着 JVM 已检测到 JVM 在垃圾收集器 CPU 总时间 运行 上花费的时间百分比过大。这就是杀死 GC 的原因……不是 运行 内存不足。
“GC 开销限制”的基本原理是当堆接近满时,GC 会频繁运行并且每次设法回收越来越少的内存。当您进入 GC“死亡螺旋”时,最好迅速拔掉插头,而不是让应用程序一直磨到它的最终故障点。
无论如何...这意味着您计算堆已满时分配了多少内存的启发式方法可能不正确。
你的问题现在几乎已经被其他人回答了,但出于兴趣我做了数学计算:-)
表示here:
This value must a multiple of 1024 greater than 2 MB.
5000000 不是 1024 的倍数。我的猜测是该参数四舍五入为 1024 的倍数,其他答案证实了这一点。您的总内存(不仅仅是声明的堆 space)是 5767168,即 5632 * 1024
。它在运行时从最初的 5242880 扩大到适合不断增长的堆 space。请注意 -Xmx
声明最大值,因此不一定立即分配。在 this source 的帮助下,我们可以估计您的内存使用情况(假设 64 位):
4878000
字节为字节
4878 * 24 = 117072
字节数组开销
- 其他创建的对象和字符串的几个字节
- 由于垃圾收集而导致的一点内存波动(至少您每次迭代创建的字符串可能会被丢弃)
所以数组占用 4995072 字节(巧合?)已经是 1024 的倍数了。但是其他对象仍然有开销。所以堆 space 是大于 4995072 且小于 5767168 的 1024 的倍数。考虑到最后的空闲 space,这给剩余的堆和非堆内存使用留下了 228808 字节,这听起来像是一个合理的数字。
终于有空了就一句话space留在最后。 JVM 不一定会在崩溃前填满所有内存。如果内存接近 运行 out,垃圾收集会更频繁地运行。如果这占用了整个运行时间的一定百分比,the JVM exits,这发生在你的情况下。
此 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)
查看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 字节。插入这些值给出 6324224 字节的实际 MaxHeapSize,这将与您看到的数字很好地对应。
注意:我只是简单地查看了 JVM 代码,所以我可能遗漏了一些东西,但答案似乎与您所看到的相加。
is -Xmx a hard limit?
这取决于你所说的“困难”是什么意思。如果你的意思是“不能被程序改变”那么是的。如果您的意思是“精确”,那么不是。垃圾收集器使用的各种 space 的大小是字节 2 的某个幂的倍数。显然,JVM 向上舍入而不是向下舍入。
Example program which allocates 1000 byte arrays
How should these numbers be understood?
一个 1000 字节的 Java 数组并不正好占用 1000 字节:
有一个对象头,包括 space 32 位
length
字段。 (通常总共 3 x 32 位)。堆节点以 2^N 字节的倍数分配。 (通常为 2^3 == 8)
然后你会问是什么导致了 OutOfMemoryError
。您可能认为这是因为堆已满。但是,在这种情况下,消息显示“超出 GC 开销限制”。这意味着 JVM 已检测到 JVM 在垃圾收集器 CPU 总时间 运行 上花费的时间百分比过大。这就是杀死 GC 的原因……不是 运行 内存不足。
“GC 开销限制”的基本原理是当堆接近满时,GC 会频繁运行并且每次设法回收越来越少的内存。当您进入 GC“死亡螺旋”时,最好迅速拔掉插头,而不是让应用程序一直磨到它的最终故障点。
无论如何...这意味着您计算堆已满时分配了多少内存的启发式方法可能不正确。
你的问题现在几乎已经被其他人回答了,但出于兴趣我做了数学计算:-)
表示here:
This value must a multiple of 1024 greater than 2 MB.
5000000 不是 1024 的倍数。我的猜测是该参数四舍五入为 1024 的倍数,其他答案证实了这一点。您的总内存(不仅仅是声明的堆 space)是 5767168,即 5632 * 1024
。它在运行时从最初的 5242880 扩大到适合不断增长的堆 space。请注意 -Xmx
声明最大值,因此不一定立即分配。在 this source 的帮助下,我们可以估计您的内存使用情况(假设 64 位):
4878000
字节为字节4878 * 24 = 117072
字节数组开销- 其他创建的对象和字符串的几个字节
- 由于垃圾收集而导致的一点内存波动(至少您每次迭代创建的字符串可能会被丢弃)
所以数组占用 4995072 字节(巧合?)已经是 1024 的倍数了。但是其他对象仍然有开销。所以堆 space 是大于 4995072 且小于 5767168 的 1024 的倍数。考虑到最后的空闲 space,这给剩余的堆和非堆内存使用留下了 228808 字节,这听起来像是一个合理的数字。
终于有空了就一句话space留在最后。 JVM 不一定会在崩溃前填满所有内存。如果内存接近 运行 out,垃圾收集会更频繁地运行。如果这占用了整个运行时间的一定百分比,the JVM exits,这发生在你的情况下。