必须双设容器层与jvm层参数:容器用--memory=2g--memory-swap=2g,jvm配-xmx1200m-xx:maxmetaspacesize=256m-xx:+usecontainersupport,并用--cpus=1.5配合gc线程调优,否则易oom kill或cpu抢占。

直接在启动容器时加参数就能生效,关键不是“能不能设”,而是“设了但 JVM 或内核不认”——尤其 Java 应用最容易踩坑。
内存限制:容器层 + JVM 层必须双设
只用 -m 2g 不够。Docker 会限制 cgroups 内存上限,但旧版 JDK(8u131 之前 / 9/10)默认读宿主机总内存,可能把堆设成 16G,结果一超限就被 OOM Kill。
- 容器层:用 --memory=2g --memory-swap=2g,禁用 swap 避免不可控溢出
- JVM 层:显式指定堆与非堆空间,例如 -Xmx1200m -XX:MaxMetaspaceSize=256m -XX:MaxDirectMemorySize=256m -Xss256k
- 必须启用容器感知:-XX:+UseContainerSupport(JDK 8u191+ / 10+ 默认开启;老版本需加 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap)
- 留出约 10%~15% 给容器系统开销(如 Java 进程自身、JIT、native memory),别把 2G 全给 -Xmx
CPU 限制:优先用 --cpus,慎用 --cpu-shares
--cpu-shares 是相对权重,只在 CPU 紧张时起作用,生产环境无法保障稳定性。--cpus=1.5 才是硬上限,表示最多占用 1.5 个逻辑核心的算力(比如宿主机 8 核,它最多用满 1.5 核)。
- 容器层:用 --cpus=1.5(支持小数),底层通过 cgroups 的
cpu.cfs_quota_us控制 - JVM 层:JVM 不会自动感知 --cpus,需手动调 GC 线程数,例如 -XX:ParallelGCThreads=3 -XX:ConcGCThreads=2(按分配 CPU 数反推)
- 验证是否生效:运行 docker stats 容器名,看 CPU % 是否长期 ≤ 150%;或进容器执行 cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 和 cpu.cfs_period_us,比值即为核数
Docker Compose 中配置资源限制
在 deploy.resources.limits 下设置,注意:该配置仅在 Docker Swarm 模式下生效;单机部署请改用 docker run 命令或启用 Compose 的 resources(v2.23+ 支持非 Swarm 场景)。
- 示例(v3.8+):
services:
app:
image: my-java-app
deploy:
resources:
limits:
cpus: '1.5'
memory: 2G
environment:
- JAVA_OPTS=-Xmx1200m -XX:MaxMetaspaceSize=256m -XX:+UseContainerSupport
- 环境变量 JAVA_OPTS 用于透传 JVM 参数,避免镜像内硬编码
- CI/CD 流水线中统一注入参数,比手工 run 更可靠
验证与避坑要点
设了不等于生效。几个关键检查点:
- 进容器执行 cat /sys/fs/cgroup/memory/memory.limit_in_bytes,确认输出是 2147483648(即 2G)
- 运行 java -XX:+PrintFlagsFinal -version | grep -E "MaxHeapSize|MaxMetaspaceSize",确认 JVM 实际加载的值符合预期
- 避免混用 --cpus 和 --cpu-shares;不要用 --cpuset-cpus 绑核替代 --cpus,除非有强亲和性需求
- JDK 版本低于 8u131 必须加实验性选项,否则容器内存限制对 JVM 无效











