容器内的 Java 应用可能会发生两种类型的 OOM 异常,

JVM 的 OOMJVM 的堆栈元空间等内存泄漏,导致没有足够的内存来为对象分配空间并且GC也没有空间可回收时,这时JVM会主动抛出错误并退出进程,并留下相应的错误记录。容器退出状态为exit code 137 reason: error(137表示容器收到SIGKILL信号而失败,通常是达到资源限制或探针失败

容器 OOM一般是JVM参数设置不合理,导致container_memory_working_set_bytes达到了cgroups限制,会在k8s事件中记录且容器退出状态为exit code 137 reason: OOM Killed

从容器来看

container_memory_working_set_bytes代表容器真实使用的内存量,也是判断超过limit的限制,超过limit则会导致oom;

container_memory_working_set_bytes = container_memory_usage_bytes - total_inactive_file(不活跃缓冲页);

container_memory_usage_bytes = container_memory_rss(进程实际使用的物理内存)+container_memory_cache(页面缓存)+kernel memory;

container_memory_cache = total_active_file + inactive_file;

从jvm来看

进程实际使用的物理内存container_memory_rss = JVM内存(堆栈元空间)+直接内存+其他(文件描述符、GC消耗等等);

容器真实使用的内存量container_memory_working_set_bytes = JVM内存(堆栈元空间)+直接内存+其他(文件描述符、GC消耗等等)+container_memory_cache(页面缓存)+kernel memory;

而预留内存又=直接内存+其他(文件描述符、GC消耗等等)+container_memory_cache(页面缓存)+kernel memory;

综上,容器真实使用的内存为 JVM内存(堆栈元空间)+ 预留内存(直接内存+文件描述符、GC消耗等 + 活跃的缓存页 + kernel memory)

一种解决 JVM 内存超限的方法

可以让 JVM 自动感知 docker 容器的 cgroup 限制,从而动态的调整堆内存大小。JDK8u131 在 JDK9 中有一个很好的特性,即 JVM 能够检测在 Docker 容器中运行时有多少内存可用。为了使 jvm 保留根据容器规范的内存,必须设置标志 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap。

注意:如果将这两个标志与 Xms 和 Xmx 标志一起设置,那么 jvm 的行为将是什么?

-Xmx 标志将覆盖-XX:+ UseCGroupMemoryLimitForHeap 标志。

MaxDirectMemorySize

-XX:MaxDirectMemorySize=size用于设置New I/O(java.nio) direct-buffer allocations的峰值,-XX:MaxDirectMemorySize 没显式配置的时候,NIO direct memory 可申请的空间的上限就是 -Xmx 减去一个 survivor space 的预留大小。如果不配置 -XX:MaxDirectMemorySize 并配置 -Xmx5G,则 默认 MaxDirectMemorySize 也将是 5G-survivor space 区,并且应用程序的总堆+直接内存使用量可能会增长到 5G + 5G = 10 G

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐