Pod 被 OOMKilled 的根本原因是容器 RSS+cache 超出 cgroup v1 的 memory.limit_in_bytes,而非 heap 或 request/limit 数值超限;Java 等应用堆外内存易致 RSS 暴涨,需显式约束 JVM 参数并预留 10–25% 内存缓冲。

Pod 被 OOMKilled,但 kubectl describe pod 显示 memory request 和 limit 都没超——这其实非常常见,根本原因往往不是容器总内存超限,而是容器内进程的“RSS 内存”突破了 cgroup v1 的硬限制(即 limit),而 request/limit 数值本身只是调度和资源担保依据,并不等于实际内存使用边界是否被触发。
OOMKilled 真正看的是 RSS,不是 heap 或 container top 显示的“已用”
cgroup v1(Kubernetes 默认)对 memory.limit_in_bytes 的限制对象是 Resident Set Size(RSS)+ cache(如 page cache、tmpfs),但不包括 swap(默认禁用)、未映射的虚拟内存、或 JVM 堆外内存(如 DirectByteBuffer、native code 分配)。很多应用(尤其是 Java、Go、Node.js)会分配大量堆外内存或缓存,这部分 RSS 会悄无声息地涨上去,直到触达 limit。
- 用
kubectl exec -it查当前 RSS + cache 总用量-- cat /sys/fs/cgroup/memory/memory.usage_in_bytes - 对比
memory.limit_in_bytes(同目录下),若接近或超过,就是 OOMKilled 直接原因 -
top或ps aux --sort=-rss只显示进程 RSS,但可能漏掉内核页缓存;cat /sys/fs/cgroup/memory/memory.stat中的total_rss更准确
Java 应用特别容易“看似没超限,实则爆 RSS”
JVM 默认不把堆外内存(-XX:MaxDirectMemorySize)、CodeCache、Metaspace、线程栈、JIT 编译缓存等计入 -Xmx,但它们全算进容器 RSS。比如一个 -Xmx2g 的 Java 应用,在高并发下可能额外吃掉 1.5g RSS,最终 total_rss 达到 3.5g —— 若 limit 设为 3g,就会 OOMKilled。
- 加 JVM 参数显式约束:-XX:MaxDirectMemorySize=256m -XX:ReservedCodeCacheSize=256m -XX:CompressedClassSpaceSize=256m
- 用
-XX:+UseContainerSupport(JDK 8u191+/10+)让 JVM 读取 cgroup limit 自动调堆(需配合 -XX:InitialRAMPercentage 等) - 避免用
spring-boot-devtools或热加载类库上线,它们会导致 Metaspace 持续增长
limit 不等于“安全水位”,要预留 10–25% buffer
Kubernetes 的 memory limit 是 cgroup 硬上限,一旦 RSS 触顶,内核立即 OOM kill 主进程(PID 1)。没有预警、不可捕获、不走 graceful shutdown。即使应用自身监控显示“内存使用率 70%”,只要 RSS 瞬间冲到 100%,就挂。
- 生产环境建议:limit ≥ request × 1.25,且绝对值比应用实测 peak RSS 高至少 300–500MiB
- 用
metrics-server + kubectl top pods观察历史 RSS 趋势,而非只看 request/limit 配置 - 开启
memory.swap不现实(K8s 默认禁用且不推荐),别指望靠 swap 救急
检查是否启用了 memory cgroup v2(少数新集群)
如果节点运行 cgroup v2(cat /proc/1/cgroup 显示 unified hierarchy),行为略有不同:OOM 逻辑更精细,但 memory.max 仍限制 total RSS,且 kubectl describe pod 不会自动展示 v2 指标。此时需直接进容器查 /sys/fs/cgroup/memory.max 和 /sys/fs/cgroup/memory.current。
- v2 下
memory.stat字段名变化(如rss→memory.current),工具链兼容性需验证 - 确认 kubelet 启动参数:
--cgroup-driver=systemd通常对应 v1,systemd+cgroupVersion=2配置才启用 v2 - 暂不建议在生产仓促切 v2,除非明确需要其 QoS 特性










