tanger
发布于 2024-06-06 / 1 阅读 / 0 评论 / 0 点赞

容器JVM内存配置最佳实践

容器JVM内存配置最佳实践

如果 JVM 堆空间大小设置过大,可能会导致 Linux 系统的 OOM Killer 被激活,进而结束(kill)Java 应用进程,在容器环境下可能会表现为频繁异常重启。本文介绍在容器环境下 JVM 堆参数的配置建议,以及 OOM 的相关常见问题。

通过 - XX:MaxRAMPercentage 限制堆大小(推荐)

  • 在容器环境下,Java 只能获取服务器的配置,无法感知容器内存限制。您可以通过设置-Xmx来限制 JVM 堆大小,但该方式存在以下问题:

    • 当规格大小调整后,需要重新设置堆大小参数。

    • 当参数设置不合理时,会出现应用堆大小未达到阈值但容器 OOM 被强制关闭的情况。

      说明

      应用程序出现 OOM 问题时,会触发 Linux 内核的 OOM Killer 机制。该机制能够监控占用过大内存,尤其是瞬间消耗大量内存的进程,然后它会强制关闭某项进程以腾出内存留给系统,避免系统立刻崩溃。

  • 推荐的 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

    使用容器内存。允许 JVM 从主机读取 cgroup 限制,例如可用的 CPU 和 RAM,并进行相应的配置。当容器超过内存限制时,会抛出 OOM 异常,而不是强制关闭容器。

    -XX:InitialRAMPercentage

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

    -XX:MaxRAMPercentage

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

    -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 目录,以便自动创建目录以及实现日志的持久化存储。

    说明

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

    • 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 目录,则必须保证该目录在应用启动前已存在,否则将不会产生日志文件。

通过 - Xms -Xmx 限制堆大小

  • 您可以通过设置-Xms-Xmx来限制堆大小,但该方式存在以下两个问题:

    • 当规格大小调整后,需要重新设置堆大小参数。

    • 当参数设置不合理时,会出现应用堆大小未达到阈值但容器 OOM 被强制关闭的情况。

      说明

      应用程序出现 OOM 问题时,会触发 Linux 内核的 OOM Killer 机制。该机制能够监控占用过大内存,尤其是瞬间消耗大量内存的进程,然后它会强制关闭某项进程以腾出内存留给系统,避免系统立刻崩溃。

  • 推荐的 JVM 参数设置。

    -Xms2048m -Xmx2048m -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
    

    参数说明如下。

    参数

    说明

    -Xms

    设置 JVM 初始内存大小。建议与-Xmx相同,避免每次垃圾回收完成后 JVM 重新分配内存。

    -Xmx

    设置 JVM 最大可用内存大小。为避免容器 OOM,请为系统预留足够的内存大小。

    -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 目录,以便自动创建目录以及实现日志的持久化存储。

  • 推荐的堆大小设置。

    内存规格大小

    JVM 堆大小

    1 GB

    600 MB

    2 GB

    1434 MB

    4 GB

    2867 MB

    8 GB

    5734 MB

通过 ossutil 下载堆转储文件

  1. 挂载容器日志目录至 NAS。具体操作,请参见设置 NAS 存储

  2. 设置 JVM 参数。

    其中 Dump 文件路径 /home/admin/nas 为 NAS 挂载目录:

    -Xms2048m -Xmx2048m -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
    
  3. 当应用发生 OOM 时,会生成堆转储文件到 NAS 挂载目录,您可以利用 ossutil 工具,将该 Dump 文件下载到本地进行分析。具体操作,请参见通过日志上传下载诊断应用

常见问题

容器出现 137 退出码的含义是什么?

当容器使用内存超过限制时,会出现容器 OOM,导致容器被强制关闭。此时业务应用内存可能并未达到 JVM 堆大小上限,所以不会产生 Dump 日志。建议您调小 JVM 堆大小的上限,为容器内其他系统组件预留足够多的内存空间。

为什么发生 OOM 却没有生成 Dump 文件?

当发生 OOM Killer 时,并不一定会发生 JVM OOM,所以不会生成 Dump 文件。您可以采取以下方式来避免这种情况。

  • 如果是 Java 应用,可以适当调小 JVM 的堆内存大小。具体配置,请参见本文。

堆大小和规格内存的参数值可以相同吗?

不可以。因为系统自身组件存在内存开销,所以不能将 JVM 堆大小设置为和规格内存大小相同的数值,需要为这些系统组件预留足够的内存空间。

在 JDK 8 版本下设置 - XX:MaxRAMPercentage 值为整数时报错怎么处理?

这是 JDK 8 的一个 Bug。具体信息,请参见 Java Bug Database。例如,在 JDK 8u191 版本下,设置-XX:MaxRAMPercentage=70,此时 JVM 会启动报错。

解决方案如下:

  • 方式一:设置-XX:MaxRAMPercentage70.0

    说明

    如果您使用了-XX:InitialRAMPercentage-XX:MinRAMPercentage,参数值同样不可设置为整数,需按照方式一的形式来设置。

  • 方式二:升级 JDK 版本至 JDK 10 及以上版本。

为什么 JVM 参数设置了 6 GB,但是内存使用率却很低?

虽然 JVM 参数已设置-Xms6g -Xmx6g,但是操作系统不会马上分配 6 GB 的物理内存,需要实际使用后才分配。因此,内存使用率在应用启动的时候,会相对较低,后续会出现攀爬现象。


评论