简体   繁体   English

Java堆分配是2MB的倍数

[英]Java Heap Allocation is in multiples of 2MB

I have noticed that Java heap allocation is in the multiples of 2 MB. 我注意到Java堆分配是2 MB的倍数。 For example I started the JVM with -Xmx values as 1021m, 1022m both JVMs started with the heap size of 1022m . 例如,我启动了JVM,其中-Xmx values as 1021m, 1022m两个JVM以堆大小1022m Similarly heap sizes 1023m,1024m started with 1024m . 类似地,堆大小1023m,1024m1024m开始。

The output is below: 输出如下:

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)

In both the cases it shows the MaxHeapSize as 1071644672 bytes which is 1022mb. 在这两种情况下,它将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)

In both the cases it shows the MaxHeapSize as 1073741824 bytes which is 1024mb. 在这两种情况下,它将MaxHeapSize显示为1073741824字节,即1024mb。

This behavior is similar for Xms. 这种行为与Xms类似。

Question 1: Why are heap sizes being changed from the specified values to next multiple of 2? 问题1:为什么堆大小从指定值更改为2的下一个倍数?

Question 2: Why Java does not even warn us when the JVM has actually started with a value different than the one specified 问题2:当JVM实际上以不同于指定值的值启动时,为什么Java甚至不警告我们

Question 3: Is there a flag or some way to force JVM to create a Heap of 1021mb? 问题3:是否有标志或某种方法迫使JVM创建一个1021mb的堆?

Question 4: In hypothetical situation say I have 1023mb of free memory available on my machine and I try to create an JVM with 1023mb of heap. 问题4:在假设的情况下说我的机器上有1023mb的可用内存,我尝试创建一个堆容量为1023mb的JVM。 By the above behavior it actually tries to start with 1024mb which is not available. 通过上面的行为,它实际上尝试以1024mb开始,这是不可用的。 Will the JVM creation fail? JVM创建会失败吗?

This is actually pretty interesting. 这实际上非常有趣。 So there are a couple of page sizes that are supported by the CPU at the same time , as described here . 因此,有一对夫妇的页面大小是由CPU,同时支持,如所描述这里

4KB is the usual one; 4KB是通常的一个; and the other two are called huge pages . 另外两个被称为huge pages

Looking at that, you see that one possible page size is 2MB and you could say - problem solved! 看一下,你看到一个可能的页面大小是2MB ,你可以说 - 问题解决了!

Because you know, you memory is indeed aligned in chunks of 2MB exactly from your examples. 因为你知道,你的内存确实与你的例子中的2MB块完全对齐。 And that was actually my first reaction when I read your question. 当我读到你的问题时,这实际上是我的第一反应。 But then I decided to try on my MAC. 但后来我决定尝试我的MAC。 Now the cpu I'm having is x86 that apparently support all 3 page sizes: 4KB, 2MB, 1GB. 现在我拥有的cpu是x86 ,显然支持所有3种页面大小:4KB,2MB,1GB。

What about the Mac OS itself? 那么Mac OS本身呢? I found the sources (jdk-9) of what value is actually being read for page size, that is in hotspot/src/os/bsd/vm/os_bsd.cpp . 我发现源(jdk-9)实际上正在读取页面大小的值,即在hotspot/src/os/bsd/vm/os_bsd.cpp And the actual code: 而实际的代码:

   Bsd::set_page_size(getpagesize())

I took that simple function getpagesize and run it in Xcode. 我使用了简单的函数getpagesize并在Xcode中运行它。 Surprise, surprise! 惊喜,惊喜! it's only 4KB , but heap is still aligned as in your example (by MB magnitude). 它只有4KB ,但堆仍然在你的例子中对齐(按MB大小)。

Then I remembered that there is alignment done on page_size here : share/vm/memory/heap.cpp . 然后我记得在这里对page_size进行了对齐: share/vm/memory/heap.cpp Here is the actual method: 这是实际的方法:

 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);
 }

That will make the memory grow just a bit, so that it is divisible by page_size ( 4KB in my case); 这会使内存增长一点,所以它可以被page_size整除(在我的例子中是4KB ); which exactly what you said in the comment about being divisible by 4KB . 这正是你在评论中所说的可以被4KB整除的内容。

Now to understand the next, I did quite a few searches... jdk-9-sources|grep Xmx and I got lucky! 现在要了解下一个,我做了不少搜索... jdk-9-sources|grep Xmx我很幸运! The lucky bits that I found are here: share/vm/gc/shared/collectorPolicy.cpp and an awesome comment: 我找到的幸运位在这里: 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;
 }

At this point I am not very sure that this is the only thing that makes the heap aligned on 2MB . 在这一点上,我不太确定这是唯一使堆在2MB对齐的东西 This uncertainty comes from the fact that this file is full of comments on how heap can be grown to adjust some parameters. 这种不确定性来自于这个文件充满了关于如何增加堆以调整某些参数的注释。 It would be fun understanding all those details, but extremly time consuming , so I gave up. 理解所有这些细节会很有趣,但是极其耗时 ,所以我放弃了。

What you observe is a limitation imposed by your operating system, not JVM. 您观察到的是您的操作系统而不是JVM所施加的限制。 When allocating memory for a process, operating systems usually don't operate bytes (because doing so would make allocation and especially access to memory a very slow process) - they use what's called a "memory page". 在为进程分配内存时,操作系统通常不操作字节(因为这样做会使分配,特别是对内存的访问非常缓慢) - 它们使用所谓的“内存页”。 You can read more about it starting from here . 您可以从这里开始阅读更多相关信息。

So, your operating system seems to have memory page size of 2MB, which means that when process requests to allocate some memory for itself, the least it can get is 2MB, which is exactly what happens in your case. 因此,您的操作系统似乎有2MB的内存页面大小,这意味着当处理请求为自己分配一些内存时,它可以获得的最少量是2MB,这正是您的情况。

For second part of your question, you can indeed force your JVM into having heap that is not a multiple of 2MB - by changing memory page size of your operating system (some of them do have this option). 对于问题的第二部分,您可以通过更改操作系统的内存页面大小(其中一些确实具有此选项)来强制您的JVM具有不是2MB的倍数的堆。 However, that may have unforeseen consequences, as it will affect all processes running inside that OS. 但是,这可能会产生无法预料的后果,因为它会影响在该操作系统内运行的所有进程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM