2017-04-04 81 views
5

我注意到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上獲得虛擬內存的最小數量? –

回答

2

這實際上很有趣。因此,如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

哇,很好的發現。 –

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