首页 > 系统相关 >【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(上)

【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(上)

时间:2022-12-01 12:37:43浏览次数:58  
标签:容器 Kubernetes VM XX 内存 JVM docker

背景介绍

在我们日常的工作当中,通常应用都会采用Kubernetes进行容器化部署,但是总是会出现一些问题,例如,JVM堆小于Docker容器中设置的内存大小和Kubernetes的内存大小,但是还是会被OOMKilled。在此我们介绍一下K8s的OOMKilled的Exit Code编码。


Exit Code 137

  • 表明容器收到了 SIGKILL 信号,进程被杀掉,对应kill -9,引发SIGKILL的是docker kill。这可以由用户或由docker守护程序来发起,手动执行:docker kill
  • 137比较常见,如果 pod 中的limit 资源设置较小,会运行内存不足导致 OOMKilled,此时state 中的 ”OOMKilled” 值为true,你可以在系统的dmesg -T 中看到OOM日志。

为什么我设置的大小关系没有错,还会OOMKilled?

因为我的heap大小肯定是小于Docker容器以及Pod的大小的,为啥还是会出现OOMKilled?

原因分析

这种问题常发生在JDK8u131或者JDK9版本之后所出现在容器中运行JVM的问题:在大多数情况下,JVM将一般默认会采用宿主机Node节点的内存为Native VM空间(其中包含了堆空间、直接内存空间以及栈空间),而并非是是容器的空间为标准。

例如在我的机器

docker run -m 100MB openjdk:8u121 java -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 444.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

以上的信息出现了矛盾,我们在运行的时候将容器内存设置为100MB,而-XshowSettings:vm打印出的JVM将最大堆大小为444M,如果按照这个内存进行分配内存的话很可能会导致节点主机在某个时候杀死我的JVM。

如何解决此问题
JVM 感知 cgroup 限制

一种方法解决 JVM 内存超限的问题,这种方法可以让JVM自动感知 docker 容器的 cgroup 限制,从而动态的调整堆内存大小。JDK8u131在JDK9中有一个很好的特性,即JVM能够检测在Docker容器中运行时有多少内存可用。为了使jvm保留根据容器规范的内存,必须设置标志-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap。

注意:如果将这两个标志与Xms和Xmx标志一起设置,那么jvm的行为将是什么?-Xmx标志将覆盖-XX:+ UseCGroupMemoryLimitForHeap标志。

总结一下
  • 标志-XX:+ UseCGroupMemoryLimitForHeap使JVM可以检测容器中的最大堆大小。
  • -Xmx标志将最大堆大小设置为固定大小。
  • 除了JVM的堆空间,还会对于非堆和jvm的东西,还会有一些额外的内存使用情况。
使用JDK9的容器感知机制尝试

$ docker run -m 100MB openjdk:8u131 java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseCGroupMemoryLimitForHeap \
-XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 44.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

可以看出来通过内存感知之后,JVM能够检测到容器只有100MB,并将最大堆设置为44M。我们调整一下内存大小看看是否可以实现动态化调整和感知内存分配,如下所示。

docker run -m 1GB openjdk:8u131 java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseCGroupMemoryLimitForHeap \
-XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 228.00M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

我们设置了容器有1GB内存分配,而JVM使用228M作为最大堆。因为容器中除了JVM之外没有其他进程在运行,所以我们还可以进一步扩大一下对于Heap堆的分配?

$ docker run -m 1GB openjdk:8u131 java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseCGroupMemoryLimitForHeap \
-XX:MaxRAMFraction=1 -XshowSettings:vm -version
VM settings:
Max. Heap Size (Estimated): 910.50M
Ergonomics Machine Class: server
Using VM: OpenJDK 64-Bit Server VM

在较低的版本的时候可以使用-XX:MaxRAMFraction参数,它告诉JVM使用可用内存/MaxRAMFract作为最大堆。使用-XX:MaxRAMFraction=1,我们将几乎所有可用内存用作最大堆。从上面的结果可以看出来内存分配已经可以达到了910.50M。

问题分析
  1. 最大堆占用总内存是否仍然会导致你的进程因为内存的其他部分(如“元空间”)而被杀死?
  • 答案:MaxRAMFraction=1仍将为其他非堆内存留出一些空间。

但如果容器使用堆外内存,这可能会有风险,因为几乎所有的容器内存都分配给了堆。您必须将-XX:MaxRAMFraction=2设置为堆只使用50%的容器内存,或者使用Xmx

容器内部感知CGroup资源限制

Docker1.7开始将容器cgroup信息挂载到容器中,所以应用可以从 /sys/fs/cgroup/memory/memory.limit_in_bytes 等文件获取内存、 CPU等设置,在容器的应用启动命令中根据Cgroup配置正确的资源设置 -Xmx, -XX:ParallelGCThreads等参数

在Java10中,改进了容器集成。
  • Java10+废除了-XX:MaxRAM参数,因为JVM将正确检测该值。在Java10中,改进了容器集成。无需添加额外的标志,JVM将使用1/4的容器内存用于堆。
  • java10+确实正确地识别了内存的docker限制,但您可以使用新的标志MaxRAMPercentage(例如:-XX:MaxRAMPercentage=75)而不是旧的MaxRAMFraction,以便更精确地调整堆的大小,而不是其余的(堆栈、本机…)
  • java10+上的UseContainerSupport选项,而且是默认启用的,不用设置。同时 UseCGroupMemoryLimitForHeap 这个就弃用了,不建议继续使用,同时还可以通过 -XX:InitialRAMPercentage、-XX:MaxRAMPercentage、-XX:MinRAMPercentage 这些参数更加细腻的控制 JVM 使用的内存比率。

Java 程序在运行时会调用外部进程、申请 Native Memory 等,所以即使是在容器中运行 Java 程序,也得预留一些内存给系统的。所以 -XX:MaxRAMPercentage 不能配置得太大。当然仍然可以使用-XX:MaxRAMFraction=1选项来压缩容器中的所有内存。

参考资料

标签:容器,Kubernetes,VM,XX,内存,JVM,docker
From: https://blog.51cto.com/alex4dream/5897197

相关文章

  • 比 Redis 快 25 倍的内存数据库!
    今年年中,一位前谷歌、前亚马逊的工程师推出了他创作的开源内存数据缓存系统Dragonfly,用C/C++编写,基于BSL许可(BusinessSourceLicense)分发。根据过往的基准测试结果来......
  • shell linux环境下内存压测
    目录shelllinux环境下内存压测脚本详情用法shelllinux环境下内存压测公司在测试prometheus检测node节点内存,但是不允许修改prometheus的阈值,只能用脚本模拟内存增加来......
  • Kubernetes(K8S) 配置管理 Secret 介绍
    Secret作用:加密数据(base64)存在etcd里面,让Pod容器以挂载Volume方式进行访问场景:凭证[root@k8smaster~]#echo-n'admin'|base64#创建secret[root@k8smas......
  • 内存泄露问题分析-AIDL 回调引起的内存泄露
    内存泄露问题分析一问题描述长时间前台运行车辆设置,存在内存泄漏情况(com.ts.faerie.hmi.vehicle_MEM),具体情况如下:1.电源上电,车机正常启动2.运行monkey程序13h3.绘制......
  • The easiest way to install Kubernetes on a Mac
    TheeasiestwaytoinstallKubernetesonaMacSteveFrancisSeptember29,2020 SoyouwanttoinstallKubernetesonyourMac?Theeasiest......
  • 【Java并发入门】02 Java内存模型:看Java如何解决可见性和有序性问题
    如何解决其中的可见性和有序性导致的问题,这也就引出来了今天的主角——Java内存模型。一、什么是Java内存模型?导致可见性的原因是缓存,导致有序性的原因是编译优化,那解......
  • python 循环中 无法释放占用内存
    问题python循环里无法释放占用内存即使手动删除del对象再调用gc回收也无法释放内存以为你del只能强引用的计数器设为-1但是gc回收时会重新检查代码中的引......
  • Kubernetes弃用Docker?其实不用慌
    近日,Kubernetes在1.20版本中的ChangeLog提到,将废弃Docker作为容器运行时。kubelet中的Docker支持功能现已弃用,并将在之后的版本中被删除。Kubelet之前使用的是一......
  • 博云:Kubernetes 近年影响最大版本发布,这几点值得关注
    近几年影响最大版本来袭2022年5月3日,Kubernetes1.24正式发布。这个版本的发布可以说是“姗姗来迟”和“万众瞩目”,因为此次发布对Kubernetes社区会带来深远影响。......
  • Kubernetes 1.25 发布!博云带你玩转新特性
    三个月转瞬即逝,Kubernetes如约发布了V1.25版本,新版本在诸多领域都有增强。Kubernetes1.25带来了40余项功能增强,其中13个功能成为稳定状态;10个是对于现有功能的改......