JVM 堆内存一般设多少?
更新时间:2026-09-18。问题驱动短答页。堆不是设越大越好——堆越大,单次 Full GC 扫描和停顿越长。
一句话回答
-Xms 与 -Xmx 设成相同值(避免运行时动态扩容卡顿);传统部署堆一般取物理内存的 50%~70%,必须给 Metaspace、线程栈、堆外内存(Netty/NIO DirectBuffer)、JVM 自身和 OS 页缓存留空间;容器里用 MaxRAMPercentage 取 limit 的约 50%~70%(堆外用得多要更低),否则进程总内存超 limit 会被 OOMKilled。
通用经验
| 部署 | 机器内存 | 建议堆(-Xmx) | 说明 |
|---|---|---|---|
| 物理机/VM | 4G | 1.5~2.5G | 留给 OS/Metaspace/堆外 |
| 物理机/VM | 8G | 4~5G | 常见微服务配置 |
| 物理机/VM | 16G | 8~11G | 看堆外/同机进程 |
| 容器 | limit 2G | ~1~1.3G | 堆外多的应用取 ~50% |
| 容器 | limit 4G | ~2.5~3G | 用百分比更省心 |
必须预留的非堆内存
进程总内存 ≈ 堆(-Xmx)
+ Metaspace(默认无硬上限,建议 -XX:MaxMetaspaceSize=256m 左右)
+ 线程栈(线程数 × -Xss,默认 512k~1m,几百线程就几百 MB)
+ 堆外/直接内存(Netty、NIO ByteBuffer,受 -XX:MaxDirectMemorySize 约束)
+ JVM 本身 + CodeCache + GC 结构容器内若只盯 -Xmx 设满 limit,上面这些一涨,容器 RSS 超限就会被内核杀掉(表现为 Pod 重启而非 Java OOM)。
推荐配置
# 传统部署:堆固定 + 限制 Metaspace
java -Xms4g -Xmx4g \
-XX:MaxMetaspaceSize=256m \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/ \
-jar app.jar
# 容器(JDK 8u191+/JDK 10+ 能感知 cgroup)
java -XX:MaxRAMPercentage=60.0 \
-XX:InitialRAMPercentage=60.0 \
-XX:MaxMetaspaceSize=256m \
-jar app.jarJDK 8u191 之前的老版本在容器里识别不到 cgroup limit,会按宿主机内存算默认堆,极易被 OOMKilled,要么升级,要么显式写死 -Xmx。容器内存限制与排查见 P2「JVM 容器内存限制」一文。
两个决策原则
- 堆大 vs 多实例:单实例 16G 堆的 GC 停顿远长于 2×8G。对延迟敏感的服务,更倾向「单机多实例 / 多 Pod + 每堆适中」,让单次 GC 更短。
- 按数据定,而不是拍脑袋:用
jstat/监控看常态存活对象大小(Full GC 后的老年代占用),堆设为「稳态存活 × 2~3 倍」较合理,既留缓冲又不过度。
坑
-Xms≠-Xmx:堆增长和向 OS 申请内存会带来抖动,生产设相等。- 容器把堆设成等于 limit:Metaspace/线程栈/堆外一算就超限被杀。
- 32G 以上超大堆不评估 GC:可能得到秒级 Full GC,超大堆考虑 G1/ZGC 或拆分服务。
- 忽略直接内存:大量用 Netty 时堆外可能比堆还吃内存,用 NMT(
-XX:NativeMemoryTracking=summary)核对。
分代结构和 GC 行为见 JVM 运行时内存与堆分代;GC 选型见 G1 与 CMS 的区别。
总结
JVM 堆设置的核心是 -Xms 与 -Xmx 生产设成相等,大小取物理机内存的 50%~70%、容器内用 MaxRAMPercentage 取 limit 的 50%~70%(堆外用得多再降),因为进程内存还包括 Metaspace、每线程栈、Netty/NIO 堆外内存和 JVM 自身,设满会在容器里被 OOMKilled;具体取值按「Full GC 后稳态存活堆的 2~3 倍」估算,延迟敏感场景宁可多实例小堆以缩短 GC 停顿,同时务必显式限制 Metaspace、开启 OOM 自动 dump,并使用能感知 cgroup 的较新 JDK 或在老版本写死 -Xmx。