--memory和--cpus是最直观可控的硬限制参数:--memory=512m设内存上限,超限触发OOM Killer;--cpus=1.2限CPU时间为每100ms最多120ms,行为可预测。

直接用 --memory 和 --cpus 就行,别碰 --cpu-shares 或 --memory-reservation 除非真有需要
--memory 和 --cpus 是最直观、最可控的两个参数。它们对应 cgroups 的硬限制,行为可预测:超了就杀进程(内存)或强制节流(CPU)。而 --cpu-shares 只在 CPU 竞争时生效,空闲时容器照样跑满核;--memory-reservation 是软限制,内核不强制执行,监控也难抓到效果。
-
--memory=512m表示容器最多用 512MB 物理内存,超限立刻被 OOM Killer 杀掉 -
--cpus=1.2表示容器每 100ms 调度周期内最多用 120ms CPU 时间,相当于 1.2 核,不会抢其他容器资源
别用 --memory=512m --memory-swap=512m 这种写法——它禁用 swap,但一旦应用突发申请内存失败,容易提前崩溃;更推荐 --memory=512m --memory-swap=1g,留 512MB swap 缓冲,给 OOM Killer 多一点判断余地。
--memory 设置太小会导致容器启动失败或秒退
Docker 不会校验你设的内存是否够用,只管往 cgroup 里写值。很多基础镜像(比如 openjdk:17-jre-slim)光 JVM 启动就要 300MB+,如果你设 --memory=256m,容器大概率在 entrypoint 执行前就被 OOM Kill,docker ps -a 看到状态是 Exited (137),日志为空。
- 先用
docker run -it --rm openjdk:17-jre-slim java -XshowSettings:vm -version查 JVM 默认堆大小 - 再加至少 100–200MB 余量(类加载、元空间、直接内存)
- 生产环境建议基于压测时的
docker stats峰值 + 20% 上浮设限
注意:--memory 限制的是整个容器进程组的 RSS + cache,不是单个 Java 进程的 -Xmx。两者要协同设置,否则 -Xmx 设太高会反复触发 OOM。
--cpus 的小数精度实际取决于宿主机 CPU 频率和调度延迟
--cpus=0.3 理论上是 30% 的单核时间,但 Linux CFS 调度最小单位是微秒级,且受 CPU 频率动态调整(如 Intel SpeedStep)、中断处理、上下文切换开销影响。实测中,0.1–0.2 的值往往不稳定,容器 CPU 使用率抖动很大,甚至长期卡在 0%。
- 低于 0.5 的配额慎用,优先考虑合并服务或换轻量运行时(如 GraalVM native image)
-
--cpus=1.0比--cpu-shares=1024更可靠:前者绝对限制,后者只是“和其他容器抢时”的权重 - 如果要绑核(比如避免 NUMA 跨节点访问),必须用
--cpuset-cpus="0,2",--cpus不提供绑定能力
验证限制是否生效,别只信 docker stats
docker stats 显示的是采样均值,有延迟、会平滑,且不反映瞬时超限。真正要看限制有没有起作用,得进 cgroup:
- 查内存硬限:
docker exec mem-limited cat /sys/fs/cgroup/memory.max(cgroup v2)或/sys/fs/cgroup/memory/memory.limit_in_bytes(v1) - 查 CPU 配额:
docker exec cpu-limited cat /sys/fs/cgroup/cpu.max(v2)或/sys/fs/cgroup/cpu/cpu.cfs_quota_us和cpu.cfs_period_us(v1)
如果看到 max 是 max(v2)或 -1(v1),说明限制根本没写进去——常见原因是 Docker daemon 启动时没启用 cgroup v2,或宿主机内核太老(<5.4)。
最易被忽略的一点:cgroup v1 和 v2 的路径、文件名、语义都不一样,Docker CLI 参数虽然统一,但底层行为依赖宿主机配置。运行前先确认 docker info | grep "Cgroup Driver",别等线上出问题才查。


















