1 原因分析
线上配置的重要配置参数为:
最大堆内存 | 最小堆内存 | docker内存 |
---|---|---|
2G | 2G | 2G |
可以看到堆内存和docker配置的内存一样大。docker内存分配=docker使用+JVM堆使+Matespace元数据区,因为JVM感知获取不到docker内存限制,docker限制内存为2G的话,能够分配给JVM堆内存必然小于2G,如果java需要申请2G内存的话必然无法通过,这时候docker进程会被宿主机杀死,docker容器就会不断重启
2 如何解决
为了适配docker,java10引入参数-XX:+UseContainerSupport
,默认值为true
,该参数能使JVM读取到可用的CUP、内存等资源。
通过MaxRAMPercentage
、InitialRAMPercentage
、MinRAMPercentage
可以设置JVM能够使用容器内存占比,比如docker分配了1GB内存,设置的参数为-XX:InitialRAMPercentage=50.0 和 -XX:MaxRAMPercentage=80.0那么JVM启动使用堆内存为50%*1024=512MB,最大可用堆内存为80%*1024=819.2MB。-Xmx -Xms已经成为过时配置。
另外,java8将永久代移除了(所以-XX:PermSize和-XX:MaxPermSize也被移除了),使用matespace代替,-XX:MetaspaceSize和-XX:MaxMetaspaceSize用来设置元数据初始和最大值,默认初始值20M左右,最大值为-1
,也就是不限制最大值,最大值仅跟宿主机剩余内存有关,如果不调整最大值,且在宿主机内存充足的情况下,matespace永远不会出现OOM。如果设置了最大值,当拓展到该值就会进行FGC。由于FGC代价高,建议两个值设置一样大,并且要比默认值大,例如:内存为4G就,设置为128M。如果MaxMetaspaceSize设置过小,可能会导致OOM错误