我注意到Java堆分配是2 MB的倍数。例如,我使用-Xmx values as 1021m, 1022m启动了JVM,两个JVM都以1022m的堆大小启动了。同样,堆大小1023m,1024m1024m开头。

输出如下:

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:当JVM实际上以不同于指定值的值启动时,为什么Java甚至都没有警告我们

问题3:是否有某种标志或某种方式可以迫使JVM创建1021mb的堆?

问题4:在假设的情况下,说我的机器上有1023mb的可用内存,我尝试用1023mb的堆创建一个JVM。通过上述行为,它实际上尝试从不可用的1024mb开始。 JVM创建会失败吗?

最佳答案

这实际上很有趣。因此,如here所述,CPU 同时支持有几个页面大小。
4KB是通常的一种。其他两个称为huge pages

看着那,您会看到一种可能的页面大小是2MB,并且您可以说-问题已解决!

如您所知,根据示例,您的内存确实按2MB的块对齐。这实际上是我阅读您的问题时的第一 react 。但是后来我决定尝试使用我的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对齐的唯一方法。这种不确定性来自以下事实:该文件是关于如何增长堆以调整某些参数的注释的完整。了解所有这些细节会很有趣,但是非常耗时,所以我放弃了。

09-11 18:59