1. JVM 感知容器资源
Java 应用部署在 Kubernetes 集群里,每个容器只运行一个进程, JVM 的启动命令是打包在镜像文件里的。
常规的方式是采用 -Xmx4g -Xms2g
这样的参数来指定 JVM 堆的最大、最小尺寸,如果需要调整堆大小就需要重新打包镜像。
为了避免因为修改堆大小而重新打包,从 JDK 8u191 版本开始支持 JVM 感知容器资源限制,这样在调整 JVM 内存分配时就不需要重新打包镜像文件,采用下面的参数来使 JVM 在启动时感知到容器的资源限制,并设定堆的大小:
-XX:+UseCGroupMemoryLimitForHeap
-XX:InitialRAMPercentage=60.00
-XX:MaxRAMPercentage=80.00
-XX:MinRAMPercentage=60.00
假如分配给容器的内存上限是 4G,那么上述配置,JVM 堆的初始大小和最小尺寸是 4G * 0.6
即 2.4G,最大尺寸是 4G * 0.8
即 3.2G。
2. JVM 被 oomkill
上面的配置运行一段时间后发现容器自动重启了,在 linux 下通过 dmesg
命令查看系统日志,可以看到类似下面的日志:
Aug 8 15:32:40 H-LDOCKER-01 kernel: [ pid ] uid tgid total_vm rss nr_ptes nr_pmds swapents oom_score_adj name
Aug 8 15:32:40 H-LDOCKER-01 kernel: [33775] 1001 33775 7624373 2036828 4476 32 0 -998 java
Aug 8 15:32:40 H-LDOCKER-01 kernel: Memory cgroup out of memory: Kill process 33775 (java) score 0 or sacrifice child
Aug 8 15:32:40 H-LDOCKER-01 kernel: Killed process 33775 (java) total-vm:30497492kB, anon-rss:8134056kB, file-rss:13256kB