容器中Java 程序OOMKilled原因浅析
背景:
业务的容器化刚刚搞完,线上开始告警,容器重启,容器重启。describe pod 查看原因是OOMKilled
分析:
OOMKilled 是pod 中的进程使用的内存超过了.spec.containers[*].resources.limits.memory中定义的内存限制,在超出限制后, kubernetes 会向容器中的进程(pid=1)发送kill -9 信号。kill -9 信号对于进程来说是不可捕捉的,进程无法在收到-9 信号后优雅的退出。 这对于业务来说是有损的。那么为啥进程会超过容器的limit 限制呢?
查看容器中进程的启动参数:
java -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -XX:MetaspaceSize=128m -jar bxr-web-1.0.jar
查看容器的limit限制
k8s-master-01#kubectl get pods -n calculation bxr-web-dd656458b-8m4fb -o=custom-columns=name:.metadata.name,namespace:.metadata.namespace,memory-limit:.spec.containers[0].resources.limits.memory
name namespace memory-limit bxr-web-dd656458b-8m4fb calculation 2000Mi
进程没有设置内存限制,但是这个业务之前在虚拟机上运行时,配置相同,启动参数也是如此,为什么上线到容器中会经常出现OOMKilled 的情况呢。这里就需要说到docker对进程资源的限制。
docker 通过 cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。但是在java 的早期版本中(小于1.8.131),不支持读取cgroup的限制。 默认是从/proc/目录读取可用内存。但是容器中的/proc目录默认是挂载的宿主机的内存目录。即java 读取的到可用的内存是宿主机的内存。那么自然会导致进程超出容器limit 限制的问题。
验证:
起初, 我们采用为进程设置-Xmx参数来限制进程的最大heap(堆)内存。例如。 容器的limit限制为3G。 那么设置java进程的最大堆内存为2.8G,采用这种方式后,容器重启的情况少了很多,但还是偶尔会出现OOMKilled 的情况。因为-xms 只能设置java进程的堆内存。 但是其他非堆内存的占用一旦超过预留的内存。还是会被kubernetes kil掉。附java 内存结构:
JVM内存结构主要有三大块:堆内存、方法区和栈
堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;
方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);
栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。
那么有没有办法能让java 正确识别容器的内存限制呢?这里有三种方法:
- 升级java版本。Java 10支持开箱即用的容器,它将查找linux cgroup信息。这允许JVM基于容器限制进行垃圾收集。默认情况下使用标志打开它。
-XX:+UseContainerSupport
值得庆幸的是,其中一些功能已被移植到8u131和9以后。可以使用以下标志打开它们。
-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
- LXCFS,FUSE filesystem for LXC是一个常驻服务,它启动以后会在指定目录中自行维护与上面列出的/proc目录中的文件同名的文件,容器从lxcfs维护的/proc文件中读取数据时,得到的是容器的状态数据,而不是整个宿主机的状态。 这样。java进程读取到的就是容器的limit 限制。而不是宿主机内存
- -XX:MaxRAM=`cat /sys/fs/cgroup/memory/memory.limit_in_bytes` 通过MaxRAM 参数读取默认的limit限制作为java 内存的最大可用内存。同时结合-Xmx 设置堆内存大小
- [收藏] Java中用Servlet容器实现程序监听
- 关于java容器使用和Comparator小程序
- Java程序运行在Docker等容器环境有哪些新问题
- JAVA程序内存溢出问题原因分析
- java调用外部程序挂起原因
- 【浅析】Java程序比C/C++程序慢的影响因素
- VC开发程序,常见C2001,C2005等错误的原因浅析
- 如何找到 java 程序 CPU 使用率100%的原因
- Android平台和java平台 DES加密解密互通程序及其不能互通的原因
- Android平台和java平台 DES加密解密互通程序及其不能互通的原因
- 浅析Java程序异常处理的特殊情况
- Android平台和java平台 DES加密解密互通程序及其不能互通的原因
- java基础-输入输出流-大文本文件读取及写入另一文本-----(此程序print部分有bug,输出不正常,原因琢磨不清了。。。)
- 浅析Java语言慢的原因
- Java程序与C语言的区别浅析
- 关于java第一个简单程序错误原因
- 【JAVA】用java编写程序时总提示缺少方法主体或声明抽象的原因
- 【Java 并发】浅析同步容器类与并发容器类
- Java高并发程序-Chapter3 JDK并发包(第二十一讲)JDK 并发容器
- 在dos中运行java程序,出现Exception in thread "main" java.lang.NoClassDefFoundError的原因