Java 云原生中的内存问题

参考文章:https://help.aliyun.com/zh/sae/serverless-app-engine-upgrade/use-cases/sae-best-practices-for-jvm-heap-size-configuration-1

为什么堆内存未超过 Xmx 却发生了 OOM?怎么理解操作系统和JVM的内存关系?为什么程序占用的内存比 Xmx 大不少,内存都用在哪儿了?为什么线上容器内的程序内存需求更大?

JVM的堆(Heap)占用内存过大会引发如下问题:如果JVM直接运行在Linux系统,可能会导致Java进程被Linux系统的OOM Killer所终止(Kill);如果JVM运行在Docker容器环境,可能会表现为频繁异常重启。本文针对在容器环境下运行JVM的内存配置给出建议,并解决OOM(Out of Memory)的相关常见问题。

推荐使用参数:-XX:MaxRAMPercentage限制JVM使用容器内存的最大百分比

容器环境下,推荐的JVM参数设置如下:

-XX:+UseContainerSupport -XX:InitialRAMPercentage=70.0 -XX:MaxRAMPercentage=70.0 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

注意事项

  • 使用-XX:+UseContainerSupport参数需JDK 8u191+、JDK 11及以上版本。

  • -XX:+UseContainerSupport参数仅在部分操作系统上支持,具体支持情况请查阅您所使用的Java版本的官方文档。

  • 在JDK 11及之后的版本中,日志相关的参数-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:$LOG_PATH/gc.log已被废弃,请使用参数-Xlog:gc:$LOG_PATH/gc.log代替。

  • Dragonwell 11不支持${POD_IP}变量。

  • 如果您没有将/home/admin/nas容器路径挂载到NAS目录,则必须保证该目录在应用启动前已存在,否则将不会产生日志文件。

参数说明:

参数

说明

-XX:+UseContainerSupport

设置JVM检测所处容器的内存大小和处理器数量,而不是检测整个操作系统的。

JVM会使用上述检测到的信息进行资源分配,例如:-XX:InitialRAMPercentage-XX:MaxRAMPercentage所设置的百分比就是基于此信息进行计算的。

-XX:InitialRAMPercentage

设置JVM使用容器内存的初始百分比。建议与-XX:MaxRAMPercentage保持一致,推荐设置为70.0,代表JVM初始使用容器内存的70%。

-XX:MaxRAMPercentage

设置JVM使用容器内存的最大百分比。由于存在系统组件开销,建议最大不超过75.0,推荐设置为70.0,代表JVM最大使用容器内存的70%。

-XX:+PrintGCDetails

输出GC详细信息。

-XX:+PrintGCDateStamps

输出GC时间戳。日期形式,例如2019-12-24T21:53:59.234+0800。

-Xloggc:/home/admin/nas/gc-${POD_IP}-$(date '+%s').log

GC日志文件路径。需保证Log文件所在容器路径已存在,建议您将该容器路径挂载到NAS目录或收集到SLS,以便自动创建目录以及实现日志的持久化存储。

-XX:+HeapDumpOnOutOfMemoryError

JVM发生OOM时,自动生成Dump文件。

-XX:HeapDumpPath=/home/admin/nas/dump-${POD_IP}-$(date '+%s').hprof

Dump文件路径。需保证Dump文件所在容器路径已存在,建议您将该容器路径挂载到NAS目录,以便自动创建目录以及实现日志的持久化存储。