K8s 里 Java 服务 limit 设了却仍 OOMKilled,JVM 内存到底怎么配才稳?
该问题的正文、回答与评论。
背景\n我在一个 12 节点(16C64G,Kubernetes 1.28.9,containerd 1.7.13,cgroup v2)的生产集群跑一套 Java 17 / Spring Boot 3 的无状态订单服务。容器 request 1Gi / limit 2Gi,节点内存水位正常。\n\n现象\n压测到一定 QPS 后容器被 OOMKilled(dmesg 里是 cgroup OOM,不是 JVM 自己抛 OutOfMemoryError),重启后几小时内复现。kubectl describe 看到容器 RSS 在 1.9Gi 附近被打掉。说明堆外 + 元空间 + 直接内存把整个 cgroup 吃满了。\n\n已尝试的排查\n1) 加了 -XX:MaxRAMPercentage=55.0,但 OOM 依旧,只是时间从几小时拉长到半天;\n2) 单独设 -Xmx 又担心和 limit 不一致导致 cgroup 先杀;\n3) MaxMetaspaceSize=256m、MaxDirectMemorySize=256m 都显式给了,但还是超;\n4) 怀疑是堆外(Netty DirectBuffer / gRPC)或 metaspace 膨胀,但没拿到分内/堆外的精确占用。\n\n期望得到的答案形态\n希望有同样踩坑的同学给一套可复现的配置组合:MaxRAMPercentage 该取多少才在 limit 内留足堆外余量、要不要显式 Xmx、Metaspace / DirectMemory / CodeCache 各留多少;以及怎么在容器里精确量出堆内 vs 堆外(NMT / jcmd 的具体命令与输出解读)。最好能贴一个能跑的配置样例,而不是“调大 limit”这种绕过去的做法。