容器环境JVM配置
现代 Java 服务大量运行在容器中。容器环境下,JVM 配置不能只看 -Xmx,还要考虑容器内存限制、堆外内存、线程栈、元空间和系统 OOM。
# 1. 容器中的内存组成
Java 进程内存包括:
- Java 堆。
- 元空间。
- 线程栈。
- 直接内存。
- Code Cache。
- GC 内部结构。
- native 库。
- mmap。
- JVM 自身开销。
容器限制的是进程总内存,不只是 Java 堆。
# 2. 不要把 Xmx 设满容器内存
如果容器限制 2GB,不应简单设置:
-Xmx2g
因为堆外还需要内存。否则可能出现:
- 容器 OOMKilled。
- Direct buffer memory。
- 无法创建线程。
- 元空间不足。
# 3. 百分比参数
现代 JDK 支持按容器内存百分比设置堆。
常见参数:
-XX:InitialRAMPercentage=50
-XX:MaxRAMPercentage=70
这样在不同容器规格下更灵活。
具体比例要结合应用的堆外使用、线程数和 GC 需求。
# 4. 线程栈
线程很多时,栈内存不可忽略。
-Xss512k
降低栈大小可以增加可创建线程数,但过小可能导致 StackOverflowError。
更重要的是控制线程池规模,避免无限创建线程。
# 5. 直接内存
使用 Netty、NIO 或大文件传输时,要关注直接内存。
-XX:MaxDirectMemorySize=512m
如果应用大量使用堆外内存,-Xmx 要给堆外留下空间。
# 6. 容器 OOM 与 JVM OOM
JVM OOM:
- JVM 抛出
OutOfMemoryError。 - 可能生成 heap dump。
- 应用可能还能执行异常处理。
容器 OOMKilled:
- 进程被操作系统直接杀死。
- JVM 来不及生成 dump。
- 日志可能只有容器事件。
所以容器环境必须监控 RSS 和容器内存,而不只是 JVM 堆。
# 7. 配置建议
建议:
- 使用现代 JDK。
- 明确容器 memory limit。
- 堆不要吃满容器。
- 限制元空间和直接内存。
- 控制线程数。
- 开启 GC 日志。
- 配置 OOM dump,但注意磁盘。
- 监控进程 RSS。
# 8. 阶段小结
容器里的 JVM 调优重点是总内存预算。-Xmx 只是其中一块,元空间、直接内存、线程栈和 JVM 自身都要占内存。很多容器 OOM 不是堆 OOM,而是整体进程内存超过限制。
# 9. 容器内存预算图
假设容器限制 4GB:
4GB Container Memory
├─ Java Heap 2.4GB
├─ Metaspace 256MB
├─ Direct Memory 512MB
├─ Thread Stacks 256MB
├─ Code Cache / JVM Native 256MB
└─ 预留余量 约 300MB
这只是示例,不是固定比例。Netty 服务直接内存会更多,线程很多的应用线程栈会更多。
# 10. 配置示例
固定堆示例:
-Xms2400m
-Xmx2400m
-XX:MaxMetaspaceSize=256m
-XX:MaxDirectMemorySize=512m
-Xss512k
百分比堆示例:
-XX:InitialRAMPercentage=60
-XX:MaxRAMPercentage=60
百分比参数更适合多规格部署,但仍要校验堆外内存。
# 11. Kubernetes 排查点
容器 OOM 时查看:
kubectl describe pod <pod>
kubectl top pod <pod>
关注:
OOMKilled。- exit code 137。
- memory limit。
- 容器重启次数。
- JVM 是否生成 OOM dump。
如果没有 JVM OOM 日志而容器重启,优先怀疑进程总内存超过 cgroup 限制。
# 专家速查表
| 配置点 | 建议 | 风险 |
|---|---|---|
| 最大堆 | 使用 MaxRAMPercentage 或明确 Xmx | 抢占容器内存 |
| 元空间 | 关注类加载规模 | 动态类导致增长 |
| 直接内存 | Netty/NIO 明确预算 | 堆外 OOM |
| 线程栈 | 线程多时评估 Xss | 栈过大挤占内存 |
| GC 日志 | 容器内默认开启采集 | 故障无证据 |
容器内存不是只给 Java 堆使用,还包括 metaspace、direct memory、线程栈、Code Cache、JNI、本地库和进程开销。配置时要留安全余量。
# 12. Tips 快问快答
Q:容器 OOMKilled 和 JVM OOM 有什么区别?
A:JVM OOM 是虚拟机抛异常;OOMKilled 是进程被系统直接杀掉,JVM 可能来不及记录。
Q:容器中 Xmx 设置多少合适?
A:取决于堆外、线程、元空间需求。常见做法是给堆 50% 到 75% 左右起步,再压测调整。
Q:为什么设置了 MaxRAMPercentage 还会 OOMKilled?
A:因为它主要控制堆,进程还有堆外和 native 内存。
Q:Netty 服务容器内存怎么配?
A:要明确直接内存预算,不能只按堆配置。