现象1:在执行任务时,在页面上发现任务执行失败了(SprintBoot项目)
# kubectl get pod |grep podname 发现有重启的记录
#kubectl describe pod podname 发现Reason:OOMKilled,Exit Code:137
经过测试发现OOMKill的时候pod占用的内存非常接近上图的Limits memory限制的1230Mi。
现象2:pod从启动到OOM期间内存一直增长未下降过,jvm初始声明的堆空间为1.7G,运行期间EdenGen占用越来越大(增长速度比较快)直到占满触发GC,GC之后Eden Space增大了100M,old sapce大小和占用没有变化,这过程中pod内存占用没有受到GC的影响(一直缓慢增长)。后面Eden空间占用继续升高在还未再次触发GC的时候,OOMKill,pod重启了。
#kubectl top pod |grep podname 查看pod内存占用
jvm堆情况
疑问1:pod启动之后jvm就给堆分配了1.7G的空间,容器限制内存为1230MI,同时pod刚运行时占用的内存为900多M。那么pod占用的900M是哪些地方占用的,jvm分配的堆和实际占用的不是一样大的么?
假设这里的1.7G只是声明,并没有实际占用这么多。
疑问2:执行任务期间JVM堆中Eden空间占用在不断增长(速度较快),但是pod的内存占用增长的速度远比Eden空间占用增长慢,随着Eden空间占满触发 Minor GC(从年轻代空间(包括 Eden 和 Survivor 区域)回收内存)Eden空间内存回收,这里从占满1个G到63M,Pod的内存却没有下降。
单单对于现象2有找到一个解释(地址:https://stackoverflow.com/questions/54279068/kubernetes-pod-memory-usage-does-not-fall-when-jvm-runs-garbage-collection):
这里的情况和现象2基本一致。
疑问3:GC之后Eden Space扩容了100M,之后还没到触发GC,pod占用的内存就超过1230M导致pod重启了。那么pod实际占用内存由哪些部分组成,JVM占用的内存由哪些部分组成,他们之间的联系又是怎么样的?
期间忙别的事去了,这里补充一下:
疑问1:pod启动之后jvm就给堆分配了1.7G的空间,容器限制内存为1230MI,同时pod刚运行时占用的内存为900多M。那么pod占用的900M是哪些地方占用的,jvm分配的堆和实际占用的不是一样大的么?
answer:pod启动后commited内存为物理内存+交换分区,交换分区不占用真正的空间,占用的是磁盘的空间(可以了解一下虚拟内存),针对此问题这里900M是真正使用的物理内存。
疑问2:执行任务期间JVM堆中Eden空间占用在不断增长(速度较快),但是pod的内存占用增长的速度远比Eden空间占用增长慢,随着Eden空间占满触发 Minor GC(从年轻代空间(包括 Eden 和 Survivor 区域)回收内存)Eden空间内存回收,这里从占满1个G到63M,Pod的内存却没有下降。
answer:JDK1.8默认GC收集器为Parallel GC,这个回收器在GC之后不会把内存返还给OS(https://www.javacodegeeks.com/2017/11/minimize-java-memory-usage-right-garbage-collector.html),所以GC以后pod内存未下降;
针对pod内存占用增长速度比Eden空间增长慢的现象解释:Eden空间有一部分已经是在使用物理内存了,当实际占用接近实际分配的内存时,需要更多的内存,jvm从交换分区交换到物理内存,此时进程内存占用才上升
疑问3:GC之后Eden Space扩容了100M,之后还没到触发GC,pod占用的内存就超过1230M导致pod重启了。那么pod实际占用内存由哪些部分组成,JVM占用的内存由哪些部分组成,他们之间的联系又是怎么样的?
answer:jvm会根据内存使用情况动态调整内存大小,占用较高但是未触发GC时jvm调整内存(扩容或者交换),此时物理内存已经不足了,没有更多的物理内存可以分配导致OOM。pod实际占用的绝大部分就是java进程占用的物理内存(commited内存=物理内存+交换分区,一般一个pod内部就只有java一个进程),附图:
图片复制自:http://www.importnew.com/14486.html
此篇告一段落,欢迎大佬们指出不对的地方