docker容器本身需要设置容器的最大可使用内存(防止单个容器消耗节点大量的资源),跑在容器中的jvm进程也需要设置内存,防止内存占用过大被容器Kill,所以如何优雅的在容器化中设置内存是一个很有必要了解的话题。

  • jdk 8u131+ java 9+ 使用参数开启实验性功能 -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap。 开启容器化支持功能。
  • java 8u191+ java10+ 使用参数UseContainerSupport 开启容器化感知支持功能。
  • -XX:MaxRAMPercentage 限制最大heap内存占比 。
  • -XX:InitialRAMPercentage 设置初始heap内存占比,此参数如果和MaxRAMPercentage设置一样表示jvm最大与最小一致 jvm不会去伸缩内存 这也是一种普遍使用的方式。其值介于0.0到100.0之间,默认值为25。

上面的几个参数可以让jvm读取cgroup的一些数据,并进行相应的适配,这样容器内jvm超时最大内存 就自己会OOM而不是被容器Kill。关于被OOM后怎么怎么拉取以及存储OOM文件参考 在k8s中收集jvm异常dump文件到OSS

案例

以下案例已设置了容器最大可实现内存4G CPU 1核

使用参数 限制heap最大内存为80%


java -server -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMPercentage=80.0 ....

使用jmap查看heap内存情况 显示MaxHeapSize为3278mb 刚好为(4*1021)/0.8 说明jvm能感知到容器最大内存为4g 且只能分配80%的内存给heap。剩余的20%供其他的进程使用。


jmap -heap 19

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 3437232128 (3278.0MB)
NewSize = 22020096 (21.0MB)
MaxNewSize = 1145569280 (1092.5MB)
OldSize = 45088768 (43.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

JDK8+可以使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置元空间初始大小以及最大可分配大小。默认情况下,元空间最大的大小是系统内存的大小,该参数一般默认也无所谓 一般不会因为这个太大导致OOM
这也证明了在容器中获取到的内存是宿主机的内存大小而不是设置了容器限制后的大小。