问题表现

近期k8s平台上监控到java容器占用内存一直上升,直到超过容器本身分配的大小,导致容器挂掉且自动重启。如此循环往复。系统稳定性差,经常失去响应。

 

(paas平台内存监控图)

(dockerfile内容)

问题分析

程序运行过程中,jvm内存一直上涨,在排除代码问题后,很明显是gc没有起作用。该问题主要是要找到什么原因使得gc没有触发。

问题原因

经过一系列jvm监控,我们发现,该jvm的最大堆内存被设置成了90G(与当前docker容器的宿主机内存相同)!

至此豁然开朗,内存居高不下的原因就是因为jvm的最大堆内存被设置成了90G,而当前容器被分配的内存只有3.5G,堆内存即时达到3.5G,也不会超过young gc的触发阈值。

举个例子:

目前容器宿主机内存为100G,容器申请了其中的4G。当我们容器中的jvm启动时其最大堆内存被默认设置成了100G。在jvm看来,触发yonggc的条件是堆内存使用到达某个阈值,比如50G,所以当堆内存使用在不超过50G时是不会触发yonggc的。在不触发gc的前提下,jvm内存不断上涨,直到超过了分配给容器的4G,就会导致容器挂掉并且重启。

但是奇怪的点有两个:

1.jvm在启动的时候应该会自动找到当前机器的内存,即时没有设置最大堆内存,jvm也会默认将当前机器内存的1/4作为最大堆内存。

2.我们已经设置了jvm启动的最大堆内存为3G,为何没有生效?

带着问题,我们查阅了很多资料,得出如下结论:

1.docker容器中,debain系统上面的openjdk无法读取到当前容器被分配的内存,只能读取到容器所在宿主机的内存。

2.设置最大堆内存的语法为java -Xms256m -Xmx256m -jar xxxx.jar。如果使用cmd添加-Xms和-Xmx参数的话,最终的执行为java -jar xxxx.jar -Xms256m -Xmx256m。-Xms和-Xmx放在-jar的后面会导致-Xms和-Xmx不生效,且不会报错。

 

解决方案

经过这次的问题,我们的反思有两个:

1.我们对虚拟化技术与java的结合这块并不熟悉,缺乏经验

2.我们在不熟悉的的情况下,就冒然使用了debain系统和openjdk,这与我们平时的部署环境大相径庭,导致出现和更多的问题。

基于以上两点,我们也做了两个改动:

1.暂时放弃了原本的openjdk和debain系统,在新的基础镜像中我们将操作系统由debain切换为了centos7,jdk由openjdk切换为oraclejdk。新的基础镜像可能在发版速度上稍慢一些,但主要还是为了保证运行环境还是熟悉的环境,尽可能保证系统稳定和问题排查的难度(经过测试,在这套环境中,即时不设置jvm的最大堆内存,jvm也可以正确读取到当前容器被分配的内存,启动时默认以当前容器被分配内存的1/4作为最大堆内存)。

2.将原本cmd中的-Xms3g -Xmx3g,直接写在entrypoint中,让-Xms和-Xmx参数在-jar之前。

 

前后对比

 

(老版本启动后)

 

(新版本启动后)

新版本启动后内存稳定在500M左右,老版本光启动过程就会占用3.8G内存且上不封顶。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐