1.概述当我们在容器中运行Java应用程序时,我们可能希望调整参数以充分利用资源。在本教程中,我们将了解如何在运行Java进程的容器中设置JVM参数。本文将重点介绍常见的-Xmx和-Xms标志。此外,我们还将探讨与某些Java版本一起运行的容器化程序的常见问题,以及如何在对常见Java应用程序进行容器化时设置自定义标志。2、Java容器中的默认堆设置过去,JVM并不知道分配给容器的内存和CPU。Java10引入了一个新设置:+UseContainerSupport(默认启用)来解决此问题[3],并在8u191[4]中将此修复程序反向移植到Java8。现在JVM可以根据分配给容器的内存来计算它的内存。1、自动内存计算当-Xmx和-Xmx参数没有设置时,JVM会根据系统规格调整堆大小。查看堆大小:$java-XX:+PrintFlagsFinal-version|grep-Ei"maxheapsize|maxram"输出如下:openjdkversion"15"2020-09-15OpenJDKRuntimeEnvironmentAdoptOpenJDK(build15+36)OpenJDK64-BitServerVMAdoptOpenJDK(build15+36,mixedmode,sharing)size_tMaxHeapSize=4253024256{product}{ergonomic}uint64_tMaxRAM=137438953472{pdproduct}{default}uintxMaxRAMFraction=4{product}{default}doubleMaxRAMPercentage=25.000000{product}{default}size_tSoftMaxHeapSize=425302425}{ageable}ergonomic}我们看到JVM将其堆大小设置为可用RAM的大约25%。在此示例中,在16GB系统上分配了4GB。出于测试目的,创建一个名为PrintXmxXms.java的文件,内容是打印堆大小,单位MB,代码内容如下:importjava.lang.management.ManagementFactory;导入java.lang.management.MemoryMXBean;公共类PrintXmxXms{publicstaticvoidmain(String[]args){intmb=1024*1024;MemoryMXBeanmemoryBean=ManagementFactory.getMemoryMXBean();longxmx=memoryBean.getHeapMemoryUsage().getMax()/mb;longxms=memoryBean.getHeapMemoryUsage().getInit()/mb;System.out.println("初始内存(xms):"+xms+"mb");System.out.println("最大内存(xmx):"+xmx+"mb");}}假设已经安装了JDK,程序可以编译运行:$javac./PrintXmxXms.java$java-cp。PrintXmxXms在具有16GbRAM的主机上,输出为:INFO:InitialMemory(xms):254mbINFO:MaxMemory(xmx):Below4056mb,在容器中尝试。2.对于JDK8u191之前的版本,在包含PrintXmxXms.java文件的文件夹中添加如下Dockerfile:FROMopenjdk:8u92-jdk-alpineCOPY*.java/src/RUNmkdir/app\&&ls/src\&&javac/src/PrintXmxXms.java-d/appCMD["sh","-c",\"java-version\&&java-cp/appPrintXmxXms"]此处使用的容器使用旧版本的Java8,早于较新版本版本可用的容器支持。构建镜像:$sudodockerbuild-toldjava。Dockerfile中的CMD行是运行容器时默认执行的进程。由于未提供-Xmx或-XmsJVM标志,因此内存设置将是默认设置。运行容器:$sudodockerrun--rm-tioldjavaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-...)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)初始内存(xms):198mb最大内存(xmx):2814mb现在使用--memory=1g命令行标志将容器内存限制为1GB:$sudodockerrun--rm-ti--memory=1goldjavaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-...)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)InitialMemory(xms):198mbMaxMemory(xmx):2814mb输出完全一样.这证明旧的JVM不遵守容器内存限制。3.JDK8u130之后的版本使用相同的测试程序,将Dockerfile的第一行改成使用新版本的JVM8:FROMopenjdk:8-jdk-alpine然后再做测试:$sudodockerbuild-tnewjava.$sudodockerrun--rm-tinewjavaopenjdkversion"1.8.0_212"OpenJDKRuntimeEnvironment(IcedTea3.12.0)(Alpine8.212.04-r0)OpenJDK64-BitServerVM(build25.212-b04,mixedmode)Initial内存(xms):198mbMax内存(xmx):2814mb如上面的输出,使用整个docker主机内存来计算JVM堆大小。但是,如果您为容器分配1GB的RAM:$sudodockerrun--rm-ti--memory=1gnewjavaopenjdkversion"1.8.0_212"OpenJDKRuntimeEnvironment(IcedTea3.12.0)(Alpine8.212.04-r0)OpenJDK64位服务器VM(构建25.212-b04,混合模式)初始内存(xms):16mb最大内存(xmx):247mb这一次,JVM根据容器可用的1GBRAM计算堆大小。我们现在了解了JVM如何计算其默认值以及为什么您需要最新的JVM才能获得正确的默认值。3.常用基础镜像中的内存设置1.OpenJDK使用环境变量,而不是直接在容器命令上硬编码JVM标志。例如,如果你在Dockerfile中使用了JAVA_OPTS变量,你可以在启动容器时修改它:FROMopenjdk:8u92-jdk-alpineCOPY*.java/src/RUNmkdir/app\&&ls/src\&&javac/src/打印XmxXms。java-d/appENVJAVA_OPTS=""CMD["sh","-c",\"java-version\&&java$JAVA_OPTS-cp/appPrintXmxXms"]构建镜像:$sudodockerbuild-topenjdk-java。通过指定JAVA_OPTS环境变量在运行时选择内存设置:$sudodockerrun--rm-ti-eJAVA_OPTS="-Xms50M-Xmx50M"openjdk-javaopenjdkversion"1.8.0_92-internal"OpenJDKRuntimeEnvironment(build1.8.0_92-internal-alpine-r1-b14)OpenJDK64-BitServerVM(build25.92-b14,mixedmode)InitialMemory(xms):50mbMaxMemory(xmx):48mbNote:-XmxparameterandMaxmemoryreportedbyJVM之间存在细微差别。这是因为Xmx设置了内存分配池的最大大小,其中包括堆、垃圾收集器的幸存者空间和其他池。2.Tomcat9Tomcat9容器有自己的启动脚本,所以要设置JVM参数,需要用到这些脚本。bin/catalina.sh脚本需要在环境变量CATALINA_OPTS中设置内存参数。首先,您需要创建一个war包以部署到Tomcat。然后我们使用以下Dockerfile将其容器化,我们在其中声明CATALINA_OPTS环境变量:FROMtomcat:9.0COPY./target/*.war/usr/local/tomcat/webapps/ROOT.warENVCATALINA_OPTS="-Xms1G-Xmx1G"然后构建容器镜像并运行它:$sudodockerbuild-ttomcat.$sudodockerrun--nametomcat-d-p8080:8080\-eCATALINA_OPTS="-Xms512M-Xmx512M"tomcat注意:运行,通过CATALINA_OPTS的新值。如果未提供此值,将使用Dockerfile第3行给出的默认值。您可以检查应用程序的运行时参数并验证选项-Xmx和-Xms是否存在:$sudodockerexec-titomcatjps-lv1org.apache.catalina.startup.Bootstrap
