一個關於 Java 的堆記憶體的小探索

本文重在看一下探索思路,開始吧~

我們可以透過 -Xmx 或者 -XX:MaxHeapSize 來指定最大堆記憶體。如果不指定,它的預設值取決於物理記憶體大小,通常是 1/4。

物理記憶體大小是多大呢?如果遇到了容器會怎麼樣呢?我們先來做幾個實驗。

準備一臺 8G 物理記憶體的宿主機,上面啟動 512M 記憶體限制的 docker 容器,分別用不同的 JDK8 小版本進行預設堆記憶體大小的驗證。

一個關於 Java 的堆記憶體的小探索

我們可以看到,OpenJDK8u111 是 1。73G,這一定是基於宿主機記憶體大小。

OpenJDK8u131 也是 1。73G,仍然是基於宿主機記憶體大小。不過如果增加了 UseCGroupMemoryLimitForHeap 引數,則變成了 114M,可以猜測這應該是基於容器的 512M 記憶體大小。

而到了 OpenJDK8u222,即使不加剛剛的引數,也是 123。75M,可以猜測仍然是基於容器的 512M 記憶體大小。

可以得出結論,不同的 JDK8 小版本對 “物理記憶體” 的獲取是不同的,早期版本會獲取到宿主機物理記憶體,後期版本會獲取到容器的物理記憶體,中間的版本需要增加額外引數才會獲取到容器的物理記憶體。

————

為什麼會這樣呢?論證起來很簡單,只需要看下 111、131、222 這三個版本在獲取物理記憶體的地方,都做了哪寫手腳,有哪些改變,即可。

先看 OpenJDK8u111 原始碼,設定堆記憶體大小的程式碼在這裡。

arguments。cpp

一個關於 Java 的堆記憶體的小探索

跟進發現是使用 Linux 的 physical_memory 獲取物理記憶體。

一個關於 Java 的堆記憶體的小探索

繼續跟進發現就是使用 sysconf 來獲取 _SC_PHYS_PAGES 和 _SC_PAGESIZE 屬性值,相乘即可。sysconf 不知道的可以 man 一下。

一個關於 Java 的堆記憶體的小探索

————

再看 OpenJDK8u131 原始碼,這個是加了引數就能獲取到容器物理記憶體,我們看看咋弄的。

一個關於 Java 的堆記憶體的小探索

很直觀,多了個判斷分支,如果指定了這個引數,那麼就讀取一個 cgroup 下的檔案,從裡面取出 cgroup_max 的值,最後和宿主機物理記憶體取個較小值。

這就是所謂的支援容器化,就是多讀了個檔案,然後取較小值即可。

————

最後再看 OpenJDK8u222 原始碼,為什麼它啥引數都不加,也能預設獲取到的是容器的物理記憶體值呢?

set_heap_size 函式並沒有發生變化,那麼一定是 os::physical_memory 做了手腳。

一個關於 Java 的堆記憶體的小探索

可以看出,原來的 Linux::physical_memory 被擠到了下面,上面多了個 if 條件判斷,如果是容器化,那麼就讀另一個檔案獲取記憶體大小。

————

可以看出,在 111 版本只有 os::physical_memory 這一種途徑,來獲取宿主機的物理記憶體。

在後來的 131 版本增加了個 if 條件,判斷如果指定了某個引數,就取讀取 cgroup 下的檔案來獲取物理記憶體。

而再後來的 222 版本,直接改造了 os::physical_memory 方法內部,使其判斷如果是容器化,則讀取 cgroup 下的檔案來獲取物理記憶體。

而如果把 os::physical_memory 方法繼續跟進,你會發現它最終也是讀取 /proc/meminfo 這個檔案來提取資料而已。

所以,JDK 堆記憶體初始化的時候,物理記憶體的獲取到底有沒有支援容器化,這句話背後其實就是到底讀哪個檔案而已,只讀 meminfo 檔案就沒支援容器化,既讀 cgroup 檔案又讀 meminfo 檔案就是支援容器化,就這麼簡單的事情。

————

這裡繼續強調底層重要性,關於這個問題你也會搜到好多文章講,神乎其神的,而且做各種毫無意義的實驗,但就是不肯再深入一步看看原始碼。

你覺得看原始碼難,但這個問題你一旦從原始碼的角度來看,就簡單得要命,就是一個 if else 判斷 + 讀不同的檔案提取資料,僅此而已。

而且你看原始碼,也能得出一些網上部落格中沒有體現的細節。

比如說 JDK 支援容器化的版本獲取的物理記憶體就是容器的物理記憶體,這句就不完全正確,實際上時獲取宿主機和容器的物理記憶體然後取個較小值。

一個關於 Java 的堆記憶體的小探索

再比如新版本的獲取容器物理記憶體時,不再是讀取一個固定的路徑,而是透過讀取 filesystems 後解析字串提取出路徑,這就更加靈活了。

一個關於 Java 的堆記憶體的小探索

看,這解析字串是不是很噁心?稍稍變化一下格式這裡就不相容了,這些底層的東西也沒那麼神秘嘛~

好啦,一個小探索,分享給大家,希望大家以後面對問題時,能直接找到根因,而不是到處找資料,一頭霧水。

作者:低併發程式設計