OpenJDK 9中首次新增了一項實驗性功能,JVM可借助該功能檢測到自己運行在容器中,進而酌情調整內存限制。盡管過去幾年來容器技術日漸流行,但包括JVM在內的很多工具依然需要通過宿主機的參數訪問可用資源,經常會遇到內存不足的情況,并會顯示各種令人困惑的錯誤信息。與Java 9一同發布的該功能正是為了在多種使用場景中避免出現此類問題而生。
諸如Docker、Heroku或Kubernetes等容器技術實際上是一種基于Linux操作系統的輕量級虛擬機。這種虛擬機的空間占用更低,意味著可以在消耗更少資源的情況下,更快速地提供與傳統虛擬機極為類似的功能,但這種做法也有不足之處:傳統虛擬機更成熟,可模擬一整套專用硬件,并可確保大部分現有軟件可以按照預期結果運行;但容器技術使用了宿主機的硬件和操作系統,這意味著需要依賴宿主機相關信息的軟件在運行過程中可能無法感知容器本身所造成的額外局限。Netflix公司Linux容器服務(也叫做Titus)部門開發者Fabio Kung在2014年撰文介紹了這一情況,雖然時至今日,那篇文章中的部分內容已經有些過時,但依然可以幫助我們充分了解這個問題。
JVM也曾飽受這個問題困擾。如果不使用-Xmx指定內存上限,JVM會將上限設置為物理內存數的一小部分(通常為1/4,但情況可能各異),而這一結果甚至還沒有考慮到容器本身所造成的限制。Java 9中新增的這項功能可以判斷JVM是否運行在Control Group,即cgroup中(這是一種Linux技術,大部分容器會通過該技術對硬件和其他資源的使用施加強制限制),借此預防出現類似的問題。如果JVM檢測到自己運行在cgroup中,隨后會試圖確定cgroup所定義的內存限制,將該限制視作可用物理內存總量,并將其他每個參數設置為該值的一部分。這依然是一個實驗性功能,只有通過-XX:+UseCGroupMemoryLimitForHeap選項激活后方能生效。
Cgroups最早在2008年被納入Linux內核,并在2013年進行了重新設計,該技術可對資源的使用進行隔離,讓應用程序對內存、CPU、IO、網絡等資源的訪問進行控制。不同應用程序可創建自己的Control Group層次結構,并給每個Group應用不同的限制,這意味著應用程序無法事先知道自己要運行在哪個Group中。也正是因此,JVM只能根據cgroup和可能應用的內存限制進行猜測。
查看英文原文:Java 9 Will Adjust Memory Limits if Running with Docker