【问题标题】:Java Heap Allocation is in multiples of 2MBJava 堆分配是 2MB 的倍数
【发布时间】:2017-08-29 16:51:08
【问题描述】:

我注意到 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:是否有flag或某种方式强制JVM创建一个1021mb的Heap?

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

【问题讨论】:

  • 我猜这是您的操作系统而不是 JVM 的限制:操作系统通常有一个“内存页面”的概念 - 分配给进程的最小物理内存量。每个进程可以有很多页,但不能少于一页。您的操作系统恰好有 2MB 的页面大小。
  • @M.Prokhorov 您的评论几乎是正确的,唯一的更正是最小量的 虚拟 内存。说得更简单,内存页——虚拟,内存帧——物理。
  • @Eugene 如何为 Windows 和 linux 上的进程获取尽可能少的虚拟内存?
  • @Arun 你的意思是 我如何找到 windows 和 linux 的页面大小的最小值是多少? 你应该让问题更清楚一点,因为我没有明白,但可能只有我……
  • @Arun 还有.. 对于您的问题(我也希望尽快给出答案),您能指定您在哪个操作系统上运行它吗?

标签: java heap-memory


【解决方案1】:

您观察到的是您的操作系统而不是 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,如果我们正在查看热点源,那么Universe.cpp 中有一个preferred_heap_base()。我没有多少运气能破解它到底做了什么,因为有很多变量被移动(而且我远不是该代码的专家),但似乎确实对齐了大小,这可能解释了我们正在处理的这种行为与。
  • 我想我在这里找到了问题...并提交答案。
【解决方案2】:

这实际上很有趣。因此,CPU同时支持几种页面大小,如 here 所述。

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 上对齐的东西。这种不确定性来自于这个文件充满关于如何增长堆以调整某些参数的 cmets。理解所有这些细节会很有趣,但是非常耗时,所以我放弃了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2012-10-19
    • 2018-02-04
    • 1970-01-01
    • 2014-05-13
    相关资源
    最近更新 更多