Java堆分配是2MB的倍数

问题描述:

我注意到Java堆分配是2 MB的倍数。例如,我用-Xmx values as 1021m, 1022m启动JVM,两个JVM都以1022m的堆大小启动。同样堆大小1023m,1024m开始于1024mJava堆分配是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创建失败?

+1

我想这是你的操作系统而不是JVM的限制:操作系统通常有一个“内存页面”的概念 - 分配给处理的最小物理内存量。每个进程可以有多个页面,但不能少于一页。您的操作系统恰好具有2MB的页面大小。 –

+0

@ M.Prokhorov你的评论几乎是正确的,唯一的改正是少量的*虚拟*内存。简化内存页面 - 虚拟内存框架 - 物理。 – Eugene

+0

@Eugene如何在windows和linux上获得虚拟内存的最小数量? –

这实际上很有趣。因此,如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对齐堆。这种不确定性来自于这个文件是完整的的评论,关于如何增长堆来调整一些参数。这将是很有趣的理解所有这些细节,但极其耗时,所以我放弃了。

+0

哇,很好的发现。 –

您观察到的是您的操作系统而不是JVM施加的限制。 为进程分配内存时,操作系统通常不会操作字节(因为这样做会分配内存,尤其是访问内存是一个非常缓慢的进程) - 它们使用所谓的“内存页”。您可以从here开始详细了解它。

因此,你的操作系统似乎有2MB的内存页面大小,这意味着当进程请求为自己分配一些内存时,最少可以得到2MB,这正是你的情况。

对于问题的第二部分,通过更改操作系统的内存页大小(其中一些具有此选项),您的确可以强制JVM拥有2MB以内的堆数。但是,这可能会带来无法预料的后果,因为它会影响在该操作系统内运行的所有进程。

+0

不幸的是,不是这样。我在页面大小为4KB的Mac上运行它。我甚至在BSD上查看了page_size的jdk-9源代码:'Bsd :: set_page_size(getpagesize());'在'hotspot/src/os/bsd/vm/os_bsd.cpp'中。运行'getpagesize()'返回4KB,所以还有更多... – Eugene

+0

@Eugene,如果我们查看热点资源,那么在'Universe.cpp'中有一个'preferred_heap_base()'。我并没有太多的运气去确定它究竟做了什么,因为有太多的变量被移动(而且我远离该代码的专家),但似乎对齐大小,这可能解释我们正在处理的这种行为用。 –

+0

我想我在这里找到了问题...并提交了答案。 – Eugene