问题描述
添加时
-Xmx????m
对于命令行,JVM 为您提供了一个接近此值但最多可超出 14% 的堆.JVM 可以为您提供更接近您想要的数字,但只能通过反复试验.
to the command line, the JVM gives you a heap which is close to this value but can be out by up to 14%. The JVM can give you a figure much closer to what you want, but only through trial and error.
System.out.println(Runtime.getRuntime().maxMemory());
打印
-Xmx1000m -> 932184064
-Xmx1024m -Xmx1g -> 954728448
-Xmx1072m -> 999292928
-Xmx1073m -> 1001390080
我正在运行 HotSpot Java 8 更新 5.
I am running HotSpot Java 8 update 5.
很明显,堆可以在 1000000000
以上,但为什么是 -Xmx1073m
而不是 -Xmx1000m
?
Clearly, the heap can be something just above 1000000000
but why is this -Xmx1073m
instead of say -Xmx1000m
?
BTW 1g
== 1024m
这表明 1g
应该是 1024^3,比 1000^3 高 7%,但你得到比 1000^3 低 7%.
BTW 1g
== 1024m
which suggests that 1g
should be 1024^3 which is 7% higher than 1000^3 but you get something 7% lower than 1000^3.
如此之多表明我错过了关于堆如何工作的一些基本知识.如果我要求 -Xmx1000m 并且它是 1001390080
我不在乎,我会假设它需要遵守一些分配倍数,但是给你 932184064
建议对我来说,堆比我想象的要复杂.
Being off by so much suggests that I am missing something fundamental about how the heap works. If I asked for -Xmx1000m and it was 1001390080
I wouldn't care, I would assume there is some allocation multiple it needs to adhere to, but to give you 932184064
suggests to me the heap is more complicated than I can imagine.
编辑我发现
-Xmx1152m gives 1073741824 which is exactly 1024^3
所以看起来它给我的内存比我在这种情况下所要求的少了 128 MB,参见 maxMemory().
so it appears it is giving me exactly 128 MB less than I asked for in this case cf the maxMemory().
顺便说一句,128 是我最喜欢的数字.我今天在 128
街道号参加了一个会议,演讲者引用了 128
页上的一本书;)
BTW 128 is my favourite number. I was in a conference today at street number 128
and the speaker quoted a book from page 128
;)
推荐答案
差异似乎是由垃圾收集器的幸存者空间的大小造成的.
The difference appears to be accounted for by the size of the garbage collector's survivor space.
-Xmx
标志,如 docs,控制内存分配池的最大大小.内存分配池的堆部分分为Eden, Survivor, 和 Tenured 空间.如this answer中所述,有两个幸存者区域,其中只有一个可用于在任何给定时间点保存活动对象.因此,Runtime.maxMemory()
报告的可用于分配对象的总表观空间必须从总堆内存池中减去幸存空间之一的大小.
The -Xmx
flag, as described in the docs, controls maximum size of the memory allocation pool. The heap portion of the memory allocation pool is divided into Eden, Survivor, and Tenured spaces. As described in this answer, there are two survivor regions, only one of which is available to hold live objects at any given point in time. So the total apparent space available for allocating objects, as reported by Runtime.maxMemory()
, must subtract the size of one of the survivor spaces from the total heap memory pool.
您可以使用 MemoryMXBean
和 MemoryPoolMXBean
类来获取有关内存分配的更多信息.这是我写的一个简单的程序:
You can use the MemoryMXBean
and MemoryPoolMXBean
classes to get a little more information about your memory allocation. Here's a simple program I wrote:
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
public class MemTest {
static String mb (long s) {
return String.format("%d (%.2f M)", s, (double)s / (1024 * 1024));
}
public static void main(String[] args) {
System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));
MemoryMXBean m = ManagementFactory.getMemoryMXBean();
System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));
System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));
for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {
System.out.println("Pool: " + mp.getName() +
" (type " + mp.getType() + ")" +
" = " + mb(mp.getUsage().getMax()));
}
}
}
java -Xmx1024m MemTest
在 OpenJDK 7 上的输出是:
The output of this on OpenJDK 7 for java -Xmx1024m MemTest
is:
Runtime max: 1037959168 (989.88 M)
Non-heap: 224395264 (214.00 M)
Heap: 1037959168 (989.88 M)
Pool: Code Cache (type Non-heap memory) = 50331648 (48.00 M)
Pool: Eden Space (type Heap memory) = 286326784 (273.06 M)
Pool: Survivor Space (type Heap memory) = 35782656 (34.13 M)
Pool: Tenured Gen (type Heap memory) = 715849728 (682.69 M)
Pool: Perm Gen (type Non-heap memory) = 174063616 (166.00 M)
请注意,Eden + 2*Survivor + Tenured = 1024M,这正是命令行上请求的堆空间量.非常感谢 @Absurd-Mind 指出这一点.
Note that Eden + 2*Survivor + Tenured = 1024M, which is exactly the amount of heap space requested on the command line. Much thanks to @Absurd-Mind for pointing this out.
您在不同 JVM 之间观察到的差异可能是由于选择不同代的默认相对大小的不同启发式方法.如这篇文章中所述(适用于 Java 6,找不到更新的版本),您可以使用 -XX:NewRatio
和 -XX:SurvivorRatio
标志来显式控制这些设置.所以,运行命令:
The differences you observe between different JVMs are likely due to differing heuristics for selecting the default relative sizes of the various generations. As described in this article (applies to Java 6, wasn't able to find a more recent one), you can use the -XX:NewRatio
and -XX:SurvivorRatio
flags to explicitly control these settings. So, running the command:
java -Xmx1024m -XX:NewRatio=3 -XX:SurvivorRatio=6
你是在告诉 JVM:
Young:Tenured = (Eden + 2*Survivor):Tenured = 1:3 = 256m:768m
Survivor:Eden = 1:6 = 32m:192m
因此,使用这些参数,请求的 -Xmx
值与 Runtime.maxMemory()
报告的可用内存之间的差异应该是 32m,使用验证上述程序.现在您应该能够准确地预测 Runtime
报告的给定命令行参数集的可用内存,这正是您真正想要的,对吧?
So, with these parameters, the difference between the requested -Xmx
value and the available memory reported by Runtime.maxMemory()
should be 32m, which is verified using the above program. And now you should be able to accurately predict the available memory reported by Runtime
for a given set of command-line arguments, which is all you ever really wanted, right?
这篇关于为什么 -Xmx 和 Runtime.maxMemory 不一致的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!