Linux

了解 Docker 中的 Java 記憶體行為

  • June 14, 2017

我們目前在 Google Container Engine (GKE) 中執行的 Kubernetes 集群由n1-standard-1執行由 Google 維護的 Debian GNU/Linux 7.9 (wheezy) 的機器類型(1 個虛擬 CPU 和 3.75 GB 記憶體)組成。由於我們服務的負載和記憶體使用量增加,我們需要將節點升級到更大的機器類型。在我們的測試集群中嘗試這個時,我們經歷了一些(對我們來說)似乎很奇怪的事情。

JVM 應用程序在部署到 Google 節點時消耗的記憶體似乎與節點上可用的核心數成正比。即使我們將 JVM 最大記憶體 (Xmx) 設置為 128Mb,它在 1 核機器上也消耗大約 250Mb(這是可以理解的,因為 JVM 由於 GC、JVM 本身等原因消耗的記憶體超過了最大限制),但它消耗大約2 核機器 ( n1-standard-2) 上 700Mb 和 4 核機器 ( ) 上大約 1.4Gb n1-standard-4。唯一不同的是機器類型,使用相同的 Docker 鏡像和配置。

例如,如果我使用n1-standard-4機器類型通過 SSH 連接到機器並執行,sudo docker stats <container_name>我會得到以下資訊:

CONTAINER CPU %               MEM USAGE / LIMIT    MEM %               NET I/O             BLOCK I/O
k8s.name  3.84%               1.11 GB / 1.611 GB   68.91%              0 B / 0 B 

當我在本地使用完全相同的(應用程序)配置(mac osx 和 docker-machine)執行相同的Docker映像時,我看到

CONTAINER CPU %               MEM USAGE / LIMIT    MEM %               NET I/O               BLOCK I/O
name      1.49%               236.6 MB / 1.044 GB  22.66%              25.86 kB / 14.84 kB   0 B / 0 B         

這更符合我對Xmx設置的期望(據記錄,我有 8 個核心和 16Gb 的記憶體)。當我top -p <pid>在 GKE 實例上執行時,同樣的事情得到了證實,這給了我 1.1 到 1.4 Gb 的 RES/RSS 記憶體分配。

Docker 鏡像定義如下:

FROM java:8u91-jre
EXPOSE 8080
EXPOSE 8081

ENV JAVA_TOOL_OPTIONS -Dfile.encoding=UTF-8

# Add jar
ADD uberjar.jar /data/uberjar.jar

CMD java -jar /data/uberjar.jar -Xmx128m -server

我也嘗試添加:

ENV MALLOC_ARENA_MAX 4

我已經在諸如此類的幾個執行緒中看到了推薦,它似乎並沒有任何區別。我也嘗試過更改為另一個 Java 基礎映像以及使用 alpine linux,但這似乎也沒有改變。

我本地的 Docker 版本是 1.11.1,Kubernetes/GKE 中的 Docker 版本是 1.9.1。Kubernetes 版本(如果重要)是 v1.2.4。

同樣有趣的是,如果我們將 pod/容器的多個實例部署到同一台機器/節點,一些實例將分配更少的記憶體。例如,前三個可能會分配 1.1-1.4Gb 的記憶體,但隨後的 10 個容器僅分配大約 250 Mb,這大約是我期望的每個要分配的實例。問題是,如果我們達到機器的記憶體限制,前三個實例(分配 1.1Gb 記憶體的實例)似乎永遠不會釋放它們分配的記憶體。如果他們在機器承受更大的壓力時釋放記憶體,我不會擔心這一點,但因為即使在機器載入時它們也會保留記憶體分配,這會成為一個問題(因為它禁止在這台機器上調度其他容器並且因此資源被浪費)。

問題:

  1. 什麼可能導致這種行為?這是JVM問題嗎?碼頭工人問題?虛擬機問題?Linux 問題?配置問題?或者也許是一個組合?
  2. 在這種情況下,我可以嘗試做些什麼來限制 JVM 的記憶體分配?

問題出在Dockerfile. 我們像這樣啟動我們的 JVM:

CMD java -jar /data/uberjar.jar -Xmx128m -server

但是當我們切換到:

CMD java -Xmx128m -server -jar /data/uberjar.jar

這些設置被考慮在內。我仍然不明白為什麼我沒有在本地看到這個,但我很高興我們設法解決了它。

當您指定

CMD java -jar /data/uberjar.jar -Xmx128m -server

然後您打算作為 JVM 參數 ( -Xmx128m -server) 的值作為命令行參數傳遞給 .jar 文件中的 Main 類。它們可作為您public static void main(String... args)方法中的參數使用。

請注意,如果您按名稱執行主類,而不是指定執行檔,這也是正確的jar

要將它們作為 JVM 參數而不是程序的參數傳遞,您需要在-jararg 之前指定它們。

請參閱https://stackoverflow.com/questions/5536476/passing-arguments-to-jar-which-is-required-by-java-interpreter

引用自:https://serverfault.com/questions/787233