线上问题总结:运行在容器里的springboot应用,出现多次oom
项目场景:提示:这里简述项目相关背景:运行环境:k8s+docker+jdk11+springBoot公司的项目都是使用kubernate+docker来管理、运行应用。例如:项目场景:发现项目出现多次oom。排查到的原因有两个,1是分配的堆内存太小;2是代码设计原因(之后写出来总结)问题描述:提示:这里描述项目中遇到的问题:例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据APP
项目运行环境:
运行环境:k8s+docker+open-jdk11+springBoot
公司的项目都是使用kubernate+docker来管理、运行应用。
问题描述:
发现项目出现多次oom。排查到的原因有两个,1是分配的堆内存太小;2是代码设计原因(之后写出来总结)
原因分析:
进入容器,使用 jhsdb jmap --heap --pid [PID] 查看分配的堆大小
输出结果:
JVM version is 11.0.9.1+1
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 262144000 (250.0MB)
NewSize = 5570560 (5.3125MB)
MaxNewSize = 87359488 (83.3125MB)
OldSize = 11206656 (10.6875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 43319296 (41.3125MB)
used = 17549536 (16.736541748046875MB)
free = 25769760 (24.575958251953125MB)
40.5120526427761% used
Eden Space:
capacity = 38535168 (36.75MB)
used = 14510800 (13.838577270507812MB)
free = 24024368 (22.911422729492188MB)
37.655992572810376% used
From Space:
capacity = 4784128 (4.5625MB)
used = 3038736 (2.8979644775390625MB)
free = 1745392 (1.6645355224609375MB)
63.51702964469178% used
To Space:
capacity = 4784128 (4.5625MB)
used = 0 (0.0MB)
free = 4784128 (4.5625MB)
0.0% used
tenured generation:
capacity = 95477760 (91.0546875MB)
used = 69527288 (66.30638885498047MB)
free = 25950472 (24.74829864501953MB)
72.82040131649507% used
可以看到最大堆内存只有250MB,也是很奇怪为什么会这么小,之前也没怎么关注这块。
查询发现,k8s会限制容器的运行内存,所以使用 kubectl describe [pod] 查看节点的详情,可以看到容器限制的内存大小:
Containers:
your-appliction-name:
Limits:
cpu: 500m
memory: 1000Mi
Requests:
cpu: 200m
memory: 1000Mi
想不懂,限制的是1000mib,为什么实际分配的只有250mb。
查询发现:
-XX:+UseContainerSupport ,此参数用于使 JVM在分配堆大小时考虑容器内存限制,而不是主机配置。
在早期jdk版本里,应用运行在容器里,jvm无法感知到容器环境的存在,所以对容器资源的限制比如内存或者cpu等都无法生效,只能获取到服务器的配置。那么分配jvm内存超过容器限制,容器会被kill掉。
为了解决这个问题,从jdk10开始,加入了 +UseContainerSupport(默认情况下启用),通过这个特性,可以使得JVM在容器环境分配合理的堆内存。 并且,在JDK8U191版本之后,这个功能引入到了JDK 8,而JDK 8是广为使用的JDK版本。
注:这里具体jdk版本没经过验证。
-XX:MaxRAMPercentage=25(默认25),指定java堆最大大小占虚拟机可用总内存的百分比
也就是这个参数,造成 容器限制的最大内存为1000mib,java堆实际被分配到的最大为250mib。
-XX:InitialRAMPercentage =1.5(默认1.5),指定java堆初始大小占虚拟机可用总内存的百分比
所有堆内存只有250mb的原因找到了。
解决方案:
设置jvm参数:-XX:+UseContainerSupport -XX:MaxRAMPercentage=70 -XX:InitialRAMPercentage =70
把jvm堆内存占 容器限制的总内存70%。重启容器,使用jmap命令进行查询,确实发生改变。
注意:这个占比,需要根据自己的应用来进行设置,因为除堆内存外,jvm还会占用容器的部分内存,容器运行也需要一部分运行,当容器内存不足时,容器会产生OOMKill,将容器kill进行重启。
学习参考链接:
更多推荐
所有评论(0)