必须启用-xx:+usecontainersupport并配合-xx:maxrampercentage等参数使jvm感知容器内存限制,同时设置--memory硬限、禁用swap,并显式约束元空间、直接内存和线程栈等非堆区域,避免oom killer强制终止进程。

关键不是堆设多大,而是让 JVM 真正“看得到”容器的内存边界,并为非堆部分留出足够空间。只写 -Xmx1g 而不约束容器限制或非堆区域,大概率被 OOM Killer 杀掉。
必须配硬性容器内存限制(--memory)并禁用 swap
Docker 默认不强制内存上限,JVM 会读宿主机总内存(比如 32G),哪怕你只想要 1G。必须显式加:
-
--memory=1g:划出物理内存红线,超限直接终止进程 -
--memory-swap=1g:让 swap 总量 = 物理内存,等于彻底禁用 swap;漏掉这句,Docker 会默认补1g RAM + 1g Swap,反而掩盖泄漏、延迟 OOM 触发
用容器感知参数动态算堆,别硬写 -Xmx
JDK 8u191+ 和 JDK 10+ 默认支持容器感知,但需显式启用:
- 加
-XX:+UseContainerSupport(旧版需配合-XX:UnlockExperimentalVMOptions) - 用
-XX:MaxRAMPercentage=75.0:堆最多占容器内存的 75%,比如--memory=1g→ 堆约 768MB - 可选
-XX:InitialRAMPercentage=70.0:避免启动期频繁扩容堆 - 验证是否生效:进容器执行
java -XX:+PrintFlagsFinal -version | grep MaxHeapSize,数值应接近预期
显式约束所有非堆内存区域
容器内存 = 堆 + 元空间 + 直接内存 + 线程栈 + JVM 自身开销(约 10%~15%)。只控堆远远不够:
-
-XX:MaxMetaspaceSize=256m:防止类加载过多撑爆元空间 -
-XX:MaxDirectMemorySize=256m:Netty/NIO 场景必加,否则 Direct Buffer 无节制增长 -
-Xss256k:线程栈从默认 1M 降到 256k,高并发下节省显著 - 估算底线:容器限制 ≥
-Xmx × 1.3 + 100MB
选轻量基础镜像并验证 cgroup 生效
镜像越重,运行时基础开销越大:
- 优先用
openjdk:17-jre-slim或openjdk:17-alpine,避开 full JDK 和调试工具 - 进容器检查:
cat /sys/fs/cgroup/memory.max(cgroup v2)或cat /sys/fs/cgroup/memory/memory.limit_in_bytes(v1),确认值与--memory一致 - 再查
cat /sys/fs/cgroup/cpu.max或/sys/fs/cgroup/cpu/cpu.cfs_quota_us,确保 CPU 限制也落地
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











