前言Java和Docker的结合,虽然更好的解决了应用打包的问题。但是也有不兼容的地方。例如,Java无法自动发现Docker设置的内存限制和CPU限制。这样就会导致JVM无法稳定的为业务服务!容器会杀死你的JVM进程,健康检查会拉起你的JVM进程,这会导致你每天甚至数百次监控你的pod重启。我们希望Java进程在容器中运行时,Java能够自动识别容器限制并获取正确的内存和CPU信息,而不是每次都需要配置kubernetesyaml描述文件中显示的容器,还需要配置JVM参数。使用JVMMaxRAM参数或者解锁实验特性的JVM参数,将JDK升级到10+,就可以解决这个问题(也许~.~)。首先,Docker容器本质上是宿主机上的一个进程,它与宿主机共享一个/proc目录,也就是说我们在容器中看到的/proc/meminfo和/proc/cpuinfo是一样的直接在host上看到的协议,如下。Hostcat/proc/meminfoMemTotal:197869260kBMemFree:3698100kBMemAvailable:62230260kBContainerdockerrun-it--rmalpinecat/proc/meminfoMemTotal:197869260kBMemFree:3677800kBMemAvailable:62210内存信息呢?没错,是通过/proc/meminfo获取的。默认情况下,JVM的最大堆大小是系统内存的1/4。如果我们的系统是8G,那么JVM默认的Heap≈2G。Docker通过CGroups完成内存限制,/proc目录以只读形式挂载在容器中。由于Java默认情况下根本看不到CGroups限制的内存大小,因此它默认使用/proc/meminfo。中的信息作为内存信息启动。这种不兼容会导致,如果容器分配的内存小于JVM的内存,JVM进程就会被理解并杀死。内存限制不兼容。我们先来看一组测试。这里我们使用内存为188G的物理机。#free-gtotalusedusedfreesharedbuff/cacheavailableMem:1881221064在下面的测试中,我们将包括openjdk的热点虚拟机和IBM的openj9虚拟机。在下面的测试中,我们将正确识别出limit的jdk称为safe(即不会超过containerlimit,不会被kill),否则称为dangerous。对于测试用例1(OPENJDK),我们使用最新的openjdk8-12,将容器的内存限制为4G。JDK默认参数下的最大堆是多少?查看我们默认参数下有多少个版本的JDK是安全的命令如下。如果你想尝试一下,你可以使用命令。dockerrun-m4GB--rmopenjdk:8-jre-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmopenjdk:9-jre-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmopenjdk:10-jre-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmopenjdk:11-jre-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmopenjdk:12java-XshowSettings:vm-versionOpenJDK8(并没有密码器限制,26.67G)危险[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:8-jre-slimjava-XshowSettings:vm-versionVM设置:最大堆大小(估计):26.67G人体工程学机器类:服务器使用VM:OpenJDK64位服务器VMopenjdk版本“1.8.0_181”OpenJDK运行时环境(构建1.8.0_181-8u181-b13-2~deb9u1-b13)OpenJDK64-BitServerVM(build25.181-b13,mixedmode)OpenJDK8-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap(正确的密码器限制,910.50M)安全[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:8-jre-slimjava-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap-XshowSettings:vm-versionVM设置:最大。堆大小(估计):910.50MErgonomics机器类:服务器使用VM:OpenJDK64位服务器VM`openjdk版本“1.8.0_181”OpenJDK运行时环境(构建1.8.0_181-8u181-b13-2~deb9u1-b13)OpenJDK64-BitServerVM(build25.181-b13,mixedmode)OpenJDK9(并没有其他容器限制,26.67G)危险[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:9-jre-slimjava-XshowSettings:vm-versionVM设置:最大。堆大小(估计):29.97G使用VM:OpenJDK64位服务器VMopenjdkversion"9.0.4"OpenJDKRuntimeEnvironment(build9.0.4+12-Debian-4)OpenJDK64-BitServerVM(build9.0.4+12-Debian-4,混合模式)OpenJDK9-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap(正确的密码器限制,1G)安全[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:9-jre-slimjava-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap-XshowSettings:vm-versionVM设置:最大。堆大小(估计):1.00G使用VM:OpenJDK64位服务器VMopenjdk版本“9.0.4”OpenJDK运行时环境(build9.0.4+12-Debian-4)OpenJDK64位服务器VM(build9.0.4+12-Debian-4,混合模式)OpenJDK10(正确的密码器限制,1G)安全[root@xiaoke-test~]#dockerrun-m32GB--rmopenjdk:10-jre-slimjava-XshowSettings:vm-XX:MaxRAMFraction=1-version虚拟机设置:最大。堆大小(估计):1.00G使用VM:OpenJDK64位服务器VMopenjdk版本“10.0.2”2018-07-17OpenJDK运行时环境(构建10.0.2+13-Debian-2)OpenJDK64位服务器VM(build10.0.2+13-Debian-2,mixedmode)OpenJDK11(正确的密码器限制,1G)安全[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:11-jre-slimjava-XshowSettings:vm-versionVMsettings:Max.堆大小(估计):1.00G使用VM:OpenJDK64位服务器VMopenjdk版本“11.0.1”2018-10-16OpenJDKRuntimeEnvironment(build11.0.1+13-Debian-3)OpenJDK64-BitServerVM(build11.0.1+13-Debian-3,mixedmode,sharing)OpenJDK12(correctlyrecognizecontainerlimit,1G)security[root@xiaoke-test~]#dockerrun-m4GB--rmopenjdk:12java-XshowSettings:vm-version虚拟机设置:最大。堆大小(估计):1.00G使用VM:OpenJDK64位服务器VMopenjdk版本“12-ea”2019-03-19OpenJDK运行时环境(构建12-ea+23)OpenJDK64位服务器VM(构建12-ea+23,混合模式,共享)测试用例2(IBMOPENJ9)dockerrun-m4GB--rmadoptopenjdk/openjdk8-openj9:alpine-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmadoptopenjdk/openjdk9-openj9:alpine-slimjava-XshowSettings:vm-versiondockerrun-m4GB--rmadoptopenjdk/openjdk11-openj9:alpine-slimjava-XshowSettings:vm-versionopenjdk8-openj9(正确识别容器限制,3G)Security[root@小客测试~]#dockerrun-m4GB--rmadoptopenjdk/openjdk8-openj9:alpine-slimjava-XshowSettings:vm-versionVM设置:最大。堆大小(估计):3.00G人体工程学机器类:服务器使用VM:EclipseOpenJ9VMopenjdk版本“1.8.0_192”OpenJDK运行时环境(构建1.8.0_192-b12_openj9)EclipseOpenJ9VM(构建openj9-0.11.0,JRE1.8.0Linuxamd64-64位压缩引用20181107_95(启用JIT,启用AOT)OpenJ9-090ff9dcdOMR-ea548a66JCL-b5a3affe73basedonjdk8u192-b12)openjdk9-openj9(正确的密码器限制,3G)#rootdoke-test-m4GB--rmadoptopenjdk/openjdk9-openj9:alpine-slimjava-XshowSettings:vm-versionVM设置:最大。堆大小(估计):3.00G使用VM:EclipseOpenJ9VMopenjdk版本“9.0.4-adoptopenjdk”OpenJDK运行时环境(构建9.0.4-adoptopenjdk+12)EclipseOpenJ9VM(构建openj9-0.9.0,JRE9Linuxamd64-64位压缩引用20180814_248(启用JIT,启用AOT)OpenJ9-24e53631OMR-fad6bf6eJCL-feec4d2ae基于jdk-9.0.4+12)openjdk10-openj9(正确识别容器限制,3G)security[root@xiaoke-test~]#dockerrun-m4GB--rmadoptopenjdk/openjdk10-openj9:alpine-slimjava-XshowSettings:vm-versionVM设置:最大。堆大小(估计):3.00GU使用VM:EclipseOpenJ9VMopenjdk版本“10.0.2-adoptopenjdk”2018-07-17OpenJDK运行时环境(build10.0.2-adoptopenjdk+13)EclipseOpenJ9VM(buildopenj9-0.9.0,JRE10Linuxamd64-64-BitCompressedReferences20180813_102(JITenabled,AOTenabled)OpenJ9-24e53631OMR-fad6bf6eJCL-7db90eda56basedonjdk-10.0.2+13)openjdk11-openj9(正确识别容器限制,3G)security[root@xiaoke-test~]#dockerrun-m4GB--rmadoptopenjdk/openjdk11-openj9:alpine-slimjava-XshowSettings:vm-version虚拟机设置:Max.堆大小(估计):3.00G使用VM:EclipseOpenJ9VMopenjdk版本“11.0.1”2018-10-16OpenJDK运行时环境tAdoptOpenJDK(build11.0.1+13)EclipseOpenJ9VMAdoptOpenJDK(buildopenj9-0.11.0,JRE11Linuxamd64-64-BitCompressedReferences20181020_70(JITenabled,AOTenabled)OpenJ9-090ff9dcOMR-ea548a66JCL-基于f62696f378onjdk-11.0.1+13)分析分析之前先了解这样一种情况:JavaMemory(MaxRAM)=元数据+线程+代码缓存+OffHeap+Heap...一般我们只配置Heap,即使用-Xmx来指定JVM默认会使用它所能获得的最大内存的1/4作为堆的原因也是如此。安全性(即不会超过容器限制而被容器杀死)OpenJdkOpenJdk8-12可以保证这个安全特性(8和9需要特殊参数,-XX:+UnlockExperimentalVMOptions-XX:+UseCGroupMemoryLimitForHeap)。OpenJ92.IbmOpenJ9的所有版本都知道容器限制。资源利用OpenJdk自动识别容器限制后,OpenJdk将最大堆设置为容器内存的1/4左右,不会造成很大的内存浪费。当然,您可以使用另一个JVM参数配置最大堆。-XX:MaxRAMFraction=整数。下面是我整理的常用内存设置表。从中我们可以看出,JVM默认的最大堆值是MaxRAMFraction=4。随着内存的增加,堆的空闲空间越来越大。在16G容器内存的时候,java堆还不到4G。MaxRAMFractionHeapratio容器内存=1G容器内存=2G容器内存=4G容器内存=8G容器内存=16G`1≈90%910.50M1.78G3.56G7.11G14.22G2≈50%455.50M910.50M1.78G3.56G7.11G3≈33%304.00M608.00M1.19G2.37G4.74G4≈25%228.00M455.50M910.50M1.78G3.56GOpenJ9您可以从此处了解有关OpenJ9的更多信息。OpenJ9对内存利用的策略优于OpenJdk。下面是OpenJ9容器内存的策略表
