Java堆分配是2MB的倍数
我注意到Java堆分配是2 MB的倍数。例如,我用-Xmx values as 1021m, 1022m
启动JVM,两个JVM都以1022m
的堆大小启动。同样堆大小1023m,1024m
开始于1024m
。Java堆分配是2MB的倍数
的输出是下面:
C:\Users\myuser>java -Xmx1021m -XX:+PrintFlagsFinal -version | findstr "MaxHeapSize"
uintx MaxHeapSize := 1071644672
{product}
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) Client VM (build 25.121-b13, mixed mode)
C:\Users\myuser>java -Xmx1022m -XX:+PrintFlagsFinal -version | findstr "MaxHeapSize"
uintx MaxHeapSize := 1071644672
{product}
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) Client VM (build 25.121-b13, mixed mode)
在这两种情况下它显示了作为MaxHeapSize字节1071644672这是1022mb。
C:\Users\myuser>java -Xmx1023m -XX:+PrintFlagsFinal -version | findstr "MaxHeapSize"
uintx MaxHeapSize := 1073741824
{product}
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) Client VM (build 25.121-b13, mixed mode)
C:\Users\myuser>java -Xmx1024m -XX:+PrintFlagsFinal -version | findstr "MaxHeapSize"
uintx MaxHeapSize := 1073741824
{product}
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) Client VM (build 25.121-b13, mixed mode)
在这两种情况下,它都显示MaxHeapSize为1073741824字节,即1024mb。
此行为与Xms类似。
问题1:为什么堆大小从指定值更改为2的下一个倍数?
问题2:为什么Java不连警告我们当JVM实际上已经比规定
问题3的一个不同的值开始:有没有标志或某种方式来迫使JVM创建的堆1021mb?
问题4:在假设的情况下,我说我的机器上有1023mb的可用内存,我尝试用1023mb的堆创建一个JVM。通过上面的行为,它实际上试图从1024MB开始,这是不可用的。 JVM创建失败?
这实际上很有趣。因此,如here所述,CPU 在同一时间支持几个页面大小。
4KB
是通常的;另外两个叫做huge pages
。
看着这个,你会发现一个可能的页面大小是2MB
,你可以说 - 问题解决了!
因为你知道,你的记忆确实与你的例子中的2MB大小一致。这实际上是我读到你的问题时的第一反应。但后来我决定尝试我的MAC。现在我有的CPU是x86
,显然支持所有3页面大小:4KB,2MB,1GB。
Mac OS本身如何?我发现实际上正在为页面大小读取的值(jdk-9),即hotspot/src/os/bsd/vm/os_bsd.cpp
。而实际的代码:
Bsd::set_page_size(getpagesize())
我把这么简单的功能getpagesize
并运行它在Xcode。惊喜,惊喜!它只是4KB
,但堆仍按照您的示例(由MB
大小)对齐。
然后我记得在这里page_size上有对齐:share/vm/memory/heap.cpp
。下面是实际的方法:
static size_t align_to_page_size(size_t size) {
const size_t alignment = (size_t)os::vm_page_size();
assert(is_power_of_2(alignment), "no kidding ???");
return (size + alignment - 1) & ~(alignment - 1);
}
,这将使内存仅增长了一点,所以它是由PAGE_SIZE整除(4KB
在我的情况);这正是你在评论中所说的可被4KB
整除的内容。
现在了解下,我做了不少搜索... jdk-9-sources|grep Xmx
,我很幸运!我发现幸运位的位置: share/vm/gc/shared/collectorPolicy.cpp
和真棒评论:
size_t CollectorPolicy::compute_heap_alignment() {
// The card marking array and the offset arrays for old generations are
// committed in os pages as well. Make sure they are entirely full (to
// avoid partial page problems), e.g. if 512 bytes heap corresponds to 1
// byte entry and the os page size is 4096, the maximum heap size should
// be 512*4096 = 2MB aligned.
size_t alignment = CardTableRS::ct_max_alignment_constraint();
if (UseLargePages) {
// In presence of large pages we have to make sure that our
// alignment is large page aware.
alignment = lcm(os::large_page_size(), alignment);
}
return alignment;
}
在这一点上我不是很肯定这是唯一,使得上2MB
对齐堆。这种不确定性来自于这个文件是完整的的评论,关于如何增长堆来调整一些参数。这将是很有趣的理解所有这些细节,但极其耗时,所以我放弃了。
哇,很好的发现。 –
您观察到的是您的操作系统而不是JVM施加的限制。 为进程分配内存时,操作系统通常不会操作字节(因为这样做会分配内存,尤其是访问内存是一个非常缓慢的进程) - 它们使用所谓的“内存页”。您可以从here开始详细了解它。
因此,你的操作系统似乎有2MB的内存页面大小,这意味着当进程请求为自己分配一些内存时,最少可以得到2MB,这正是你的情况。
对于问题的第二部分,通过更改操作系统的内存页大小(其中一些具有此选项),您的确可以强制JVM拥有2MB以内的堆数。但是,这可能会带来无法预料的后果,因为它会影响在该操作系统内运行的所有进程。
不幸的是,不是这样。我在页面大小为4KB的Mac上运行它。我甚至在BSD上查看了page_size的jdk-9源代码:'Bsd :: set_page_size(getpagesize());'在'hotspot/src/os/bsd/vm/os_bsd.cpp'中。运行'getpagesize()'返回4KB,所以还有更多... – Eugene
@Eugene,如果我们查看热点资源,那么在'Universe.cpp'中有一个'preferred_heap_base()'。我并没有太多的运气去确定它究竟做了什么,因为有太多的变量被移动(而且我远离该代码的专家),但似乎对齐大小,这可能解释我们正在处理的这种行为用。 –
我想我在这里找到了问题...并提交了答案。 – Eugene
我想这是你的操作系统而不是JVM的限制:操作系统通常有一个“内存页面”的概念 - 分配给处理的最小物理内存量。每个进程可以有多个页面,但不能少于一页。您的操作系统恰好具有2MB的页面大小。 –
@ M.Prokhorov你的评论几乎是正确的,唯一的改正是少量的*虚拟*内存。简化内存页面 - 虚拟内存框架 - 物理。 – Eugene
@Eugene如何在windows和linux上获得虚拟内存的最小数量? –