我们遇到了一个状况,在AWS ECS上运行着一个基于Java 11的应用程序的Docker容器。该服务配置为当使用超过1.5GB RAM时被终止。
服务的内存配置如下:
{
"memory": 1500,
"memoryReservation": 1137
}
但是,我们在监控仪表板上并未观察到与之相应的内存使用增加。仪表板显示的数据平直(如图所示),这是因为Prometheus不再接收到指标数据。
我们已经尽力限制Java在堆内和堆外的内存使用量。通过Java Native Memory Tracking (NMT)检查后,确认其最大预留使用量约为1.1GB。当前的启动命令如下:
java -Xmx600m -XX:+UseG1GC -XX:MaxMetaspaceSize=220m
-XX:+PrintFlagsFinal -XX:NewRatio=1 -XX:MaxDirectMemorySize=40m
-XX:+PrintFlagsFinal -Xss512k -XX:ProfiledCodeHeapSize=70M
-XX:NonProfiledCodeHeapSize=50M -XX:NonNMethodCodeHeapSize=8M
-XX:+UseCodeCacheFlushing -XX:CompressedClassSpaceSize=32M
-XX:ReservedCodeCacheSize=128M -XX:+SegmentedCodeCache
-javaagent:/opentelemetry-javaagent.jar
-Dotel.javaagent.extensions=/opentelemetry-java-ecs-extension-all.jar
-Dotel.resource.attributes=xxx,aws.ecs.launchtype=ec2
-Dspring.profiles.active=prd-ecs -Dspring.cloud.consul.host=172.17.42.1
-Dspring.cloud.consul.config.watch.enabled=false
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dlog4j2.formatMsgNoLookups=true -Dserver.port=8080
-Daws.paramstore.enabled=true -Daws.paramstore.region=eu-west-1
-Dspring.main.banner-mode=off -jar /1234-service.jar
我们对为何容器突然使用了额外的400MB内存而JMX指标中却没有任何迹象感到困惑。
//编辑:最老运行容器的NMT输出显示:
总保留=1180115KB,已提交=700171KB,包括Java堆、类、线程、代码、GC、编译器、内部、其他、符号、原生内存跟踪、Arena Chunk、日志、参数、模块、同步器和安全点等各项的详细分配情况。
//编辑2:我们使用的Docker镜像配置如下:
基于amazoncorretto:11镜像,安装了必要的工具,下载并配置了OpenTelemetry Java Agent以及ECS扩展,并设置了用户及Java安全设置。
//编辑3:补充说明,我们还设置了环境变量MALLOC_ARENA_MAX
为4以控制内存分配。
尽管我们进行了详尽的内存管理和限制措施,仍然无法解释为何容器内存占用会超出预期且未在监控中体现。