在Logistimo,咱們的全部應用程序都是Docker化的,並在Kubernetes內以docker容器運行。咱們注意到在使用Java的容器上發生了大量重啓,而且很是隨機。Docker檢查發現該pod被OOMKiller代碼殺死:137。linux
這意味着應用程序消耗的內存比分配給容器的內存多。這聽起來不對,由於咱們使用-Xmx對Java應用程序進行了限制,而且咱們爲元空間和GC數據留下了大約20%的緩衝區做爲Kubernetes資源限制(docker容器)。docker
例如,Java進程爲2 GB,Kubernetes資源爲2.4 GB。工具
後續部分將介紹此問題以及如何詳細解決此問題。spa
第一步是檢查容器超出上述限制的緣由,顯然這些是被緩衝充分利用了。線程
使用「ps」命令能夠確認Xmx確實就位,並設置爲最大4GB。code
可是,「top」命令顯示使用的物理內存爲4.5 GB。進程
JDK 從1.8.40開始,引入了一個Native內存跟蹤器工具,它提供了Java應用程序使用的內存的詳細分解,並考慮了每一個字節。請注意,NMT工具顯示已提交,駐留可能更少。內存
實際使用=堆內存+元空間+Off堆資源
Off heap一般由類元數據,編譯代碼,線程和GC數據組成。GC數據是可變的,而其他部分應該對大多數應用程序保持靜態。此內存是本機的(是的,包括元空間),JVM使用主機上的可用內存來增加或垃圾收集此數據。it
回到手頭的問題,JVM佔用了500 MB,由於底層主機有16 GB的存儲空間。有時這個數字可能高於咱們設置的緩衝區,這將致使容器被終止。JVM不該該讀取docker容器的內存限制嗎?
事實證實,Java版本9及如下版本根本不瞭解容器/Docker(默認狀況下)。它從底層主機中獲取可用的CPU和內存。在容器內的主機上運行的每一個Java應用程序都依賴於主機配置。考慮到咱們是Kubernetes而且許多pod在單個節點上運行,這可能會致使咱們面臨的問題。
Java 10支持開箱即用的容器,它將查找linux cgroup信息。這容許JVM基於容器限制進行垃圾收集。默認狀況下使用標誌打開它。
-XX:+UseContainerSupport
值得慶幸的是,其中一些功能已被移植到8u131和9之後。可使用如下標誌打開它們。
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
較舊版本的Java讀取底層主機,而且不瞭解cgroup。這會致使容器配置和Java進程不匹配。這種不匹配在CPU和內存上。Java有一個Off堆內存組件,它有一個動態GC數據組件,能夠增加。解決此問題的最佳方法是使用最新版Java中提供的容器支持功能。不要依賴緩衝(這是浪費錢)。
若是您必須繼續使用這些主要版本並打開實驗標誌,請升級到Java 8u131 +或Java 9。更好的是,若是你能夠得到Java 10以上將對全部容器有好處。