首页 > 系统相关 >避免 OOMKilled:在 Kubernetes 环境中优化 Java 进程的内存配置

避免 OOMKilled:在 Kubernetes 环境中优化 Java 进程的内存配置

时间:2024-06-10 10:33:16浏览次数:15  
标签:reserved heap Kubernetes OOMKilled 内存 JVM committed Java

避免 OOMKilled:在 Kubernetes 环境中优化 Java 进程的内存配置

DevOps云学堂译 奇妙的Linux世界 2024-06-10 09:53 重庆 听全文 公众号关注 「奇妙的 Linux 世界」
设为「星标」,每天带你玩转 Linux !
图片

 

管理 Kubernetes Pod 中运行的 Java 进程的内存使用情况比人们想象的更具挑战性。即使使用正确的 JVM 内存配置,仍然可能会出现OOMKilled问题,您想知道为什么吗?

奇妙的Linux世界 Linux 爱好者聚集地,各种硬核干货文章和新奇内容推荐,定期发放福利红包。快加入我们,一起愉快玩耍! 229篇原创内容 公众号

长话短说

由于 JVM 仅考虑大小限制,因此无法保证 Java 进程的完整heap内存边界(堆内存);不是non-heap 内存(非堆内存),这取决于多种因素。从堆内存非堆内存的比例为 75% 开始,并密切关注内存的行为。如果事情失控,您可以调整 pod 的内存限制调整heap-to-non-heap比率来避免 OOMKilled 事故。

Context语境

我们在 Kubernetes 中运行的生产 Java 应用程序反复遇到 OOMKilled 和重启问题。尽管在 pod 和 JVM 级别都定义了内存设置,但 pod 的总内存使用量波动导致频繁重启

  • Pod 级别配置:我们最初将 Pod 的内存限制设置为 2Gi,使用以下设置:
resources:
  requests:
    memory: "2Gi"
    cpu: "4"
  limits:
    memory: "2Gi"
    cpu: "4"
  • JVM 级别配置:我们指定了 JVM 应使用的系统内存百分比,以允许 JVM 适应其环境。
-XX:MaxRAMPercentage=80.0

需要注意的是,这MaxRAMPercentage并不限制 Java 进程可以使用的总内存大小。它特指 JVMheap大小,因为堆是应用程序可访问和使用的唯一内存。通过这些设置,Pod 拥有2Gi系统内存,其中的系统内存1.6Gi被分配给堆并且0.4Gi可供非堆内存使用。(请记住,2Gi等于2 * 1024 * 1024 * 1024 = 2.15GB,因为监控指标用作GB仪表板上的内存单位。)

解决该问题的初步尝试

为了缓解OOMKilled问题,我们将 pod 的内存限制从 2Gi增加4Gi, 这确实有助于减少问题。然而,仍然存在一些问题:

  1. 为什么container_memory_working_setcontainer_memory_rss接近 100%,而 JVM 堆和非堆使用率却显着降低?

图片
2. 鉴于 Java 进程是 pod 中运行的唯一进程,为什么工作集大小 (WSS)/驻留集大小 (RSS) 内存使用量超过 JVM 总内存?
图片
3. 为什么进程内存使用率仍然接近100%,几乎达到Pod内存限制?
图片

 

分析

为什么Java总内存使用量远低于系统内存使用量?

图片
我们注意到,一旦提交的堆内存达到最大堆大小container_memory_working_setcontainer_memory_rss 就会停止增加。
图片
➊提交的 JVM Heap 一旦达到heap限制就停止增加❷ ❸当提交的内存达到限制时,WSS/RSS 的系统内存停止heap增加。根据MemoryUsage类的 Java 文档,这些指标来自:

public long getCommited()
返回提交供Java 虚拟机使用的内存量(以字节为单位)。这个内存量是保证Java虚拟机使用的。

提交的内存表示 JVM 从操作系统预先分配的内存。因此,从容器/Pod 的角度来看,WSS/RSS 使用率显得很高,而在 JVM 内,堆内存和非堆内存使用率仍然很低。
这也解释了为什么在 pod 被OOMKilled之前没有发生 OutOfMemory 异常,因为堆内存和非堆内存都没有达到 JVM 的限制。相反,JVM 会从操作系统中预先分配和保留内存,而不会轻易释放它。OpenJDK规范解释道:

G1 仅在 Full GC 或并发周期期间从 Java 堆返回内存。由于 G1 尽力完全避免 Full GC,并且仅根据 Java 堆占用和分配活动触发并发周期,因此它不会返回 Java 堆在许多情况下,除非从外部强制这样做,否则都会有内存。这种行为在资源按使用付费的容器环境中尤其不利。即使在 VM 由于不活动而仅使用其分配的内存资源的一小部分的阶段,G1 也将保留所有 Java 堆。--https://openjdk.org/jeps/346

因此,虽然Java进程的实际内存使用量可能很低,但JVM预分配的提交内存可能会高得多,并且不会立即返回给系统。

图片

为什么 WSS/RSS 内存使用量超过 JVM 总内存?

在检查了系统内存的来源和 JVM 指标后,这对我来说仍然是一个谜。

图片
系统内存 RSS 与 JVM 总提交内存之间的差距
➊系统内存 WSS 为 3.8GB
❷ JVMheap提交的内存为 3.22GB
❸ JVM 总提交的内存为 3.42GB
Pod 中运行的 JVM 的本机内存跟踪 (NMT) 报告为我们提供了 Java 进程中内存使用情况的详细细分,尤其是内存non-heap。结果与JVM Heap和JVM Total指标一致。

Native Memory Tracking:
  
  Total: reserved=5066125KB, committed=3585293KB
  -                 Java Heap (reserved=3145728KB, committed=3145728KB)
                              (mmap: reserved=3145728KB, committed=3145728KB) 
  -                     Class (reserved=1150387KB, committed=113419KB)
  -                    Thread (reserved=297402KB, committed=32854KB)
  -                      Code (reserved=253098KB, committed=73782KB)
  -                        GC (reserved=174867KB, committed=174867KB)
  -                  Compiler (reserved=2156KB, committed=2156KB)
  -                  Internal (reserved=11591KB, committed=11591KB)
  -                     Other (reserved=2690KB, committed=2690KB)
  -                    Symbol (reserved=21454KB, committed=21454KB)
  -    Native Memory Tracking (reserved=6275KB, committed=6275KB)
  -               Arena Chunk (reserved=195KB, committed=195KB)
  -                   Logging (reserved=4KB, committed=4KB)
  -                 Arguments (reserved=29KB, committed=29KB)
  -                    Module (reserved=249KB, committed=249KB)

系统内存使用 WSS/RSS已通过 Pod 中运行命令的RES内存(进程使用的常驻内存量)来确认。topJava 进程是 pod 中唯一运行的进程。

USER   PID    %CPU %MEM  VSZ      RSS      TTY  STAT START TIME   COMMAND
xxx-+      1  7.7  0.4   24751760 3818536  ?    Ssl  Jul28 340:41 /usr/java/jdk-11.0.17/bin/java -XX:MaxRAMPercentage=75.0 -XshowSettings:vm -classpath ...
xxx-+  80559  0.0  0.0   50548    3936     ?    Rs   07:02 0:00   ps -aux

因此,这两个指标都是值得信赖的,但它们之间仍然存在 300MB 左右的差距。

为什么增加 Pod 内存限制后系统内存使用率仍然接近 100%?

首先,它是resources.limits.memory确定系统内存大小而不是resources.requests.memory. 后者只是让 Kubernetes 集群找到与请求的内存匹配的节点来在其上运行 pod。
其次,如前所述,heapJVM 只能指定并严格控制内存的大小,而不能指定non/off-heap内存。因此,即使系统内存增加,non/off-heap内存使用量也可能成比例增加。
为了缓解这种情况,减少内存百分比heap可以提供更多空间non/off-heap。所以这是我们尝试的下一个选项:MaxRAMPercentage从减少80%到75%并按预期工作:WSS/RSS 下降。

图片
减少堆百分比之前:➊❷ WSS/RSS 仍接近 Pod 内存限制 (4.29GB)

图片
减少堆百分比后 ➊❷ WSS/RSS 稳定在 3.6GB,并且与 pod 内存限制 (4.29GB) 有安全余量

结论

可以使用以下方法来解决 Java 进程内存使用的不确定性并消除 pod OOMKilled问题:

  1. 从一个合理的值开始MaxRAMPercentage,这75%通常是一个很好的起点。
  2. 随着时间的推移监控heap使用情况和系统内存WSS/RSS。
  • 如果您的最大heap使用率很高(即保持在>90% 范围内),则这是增加 pod 内存限制的信号 ( resources.limits.memory)。您heap需要更多空间。
  • 如果最大heap使用率正常(即保持远低于<90%),但WSS/RSS较高且接近进程限制,请考虑减少MaxRAMPercentage为空间分配更多内存non/off-heap。
  • 监控最大值WSS/RSS以确保 Pod 内存限制始终有 5% 到 10% 的安全裕度。不要飞得太靠近太阳!

文章翻译 https://medium.com/@karthik.jeyapal/memory-settings-for-java-process-running-in-kubernetes-pod-6d0a2e092ce5

本文转载自:「 云原生百宝箱」,原文:https://url.hi-linux.com/tXoNb,版权归原作者所有。欢迎投稿,投稿邮箱: [email protected]

图片

最近,我们建立了一个技术交流微信群。目前群里已加入了不少行业内的大神,有兴趣的同学可以加入和我们一起交流技术,在 「奇妙的 Linux 世界」 公众号直接回复 「加群」 邀请你入群。

图片

你可能还喜欢

点击下方图片即可阅读

图片

GitHub 星标 10.3K:一个更适合新手的 Curl 替代工具

图片
点击上方图片,『美团|饿了么』外卖红包天天免费领

图片

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!

云计算159 软件12 开源539 云原生273 程序员451 云计算 · 目录 上一篇Kubernetes 十周年 阅读 128 ​ 喜欢此内容的人还喜欢   Github 星标 46K,这份开源系统设计指南太赞了!     我常看的号 奇妙的Linux世界   不看的原因   Rio terminal:新一代高性能终端的崛起     编程悟道   不看的原因   [Golang 1.23 前瞻] unique package 介绍     我看过的号 Go Official Blog   不看的原因   写留言       奇妙的Linux世界            

人划线

 

标签:reserved,heap,Kubernetes,OOMKilled,内存,JVM,committed,Java
From: https://www.cnblogs.com/cheyunhua/p/18240450

相关文章

  • Java历史,学习体系结构的介绍
    Java历史,体系结构的介绍整理@程序员猴哥返回到Java开发知识汇总目录java历史java在1995年推出,是目前最受欢迎的开发语言之一。什么人可以学Java,是否能学好,就业前期如何呢?软件开发,目前,未来5年还是高薪行业,人工智能不可能取代程序员。英语不好,也可以学编程(高中以上)理......
  • 网上 copy 的一段 javascript 代码 String.prototype.replaceAll = fucntion(){...}
    早些年,浏览器没有内置字符串的replaceAll()方法,就从网上copy了一段replaceAll()的实现:String.prototype.replaceAll=function(AFindText,ARepText){raRegExp=newRegExp(AFindText,"g");returnthis.replace(raRegExp,ARepText)}今天突然遇到一个问题,定位到了这段代码,我......
  • JAVA lambda表达式方法引用+构造器引用
    若Lambda体中的内容有方法已经实现了,使用“方法引用”注意:Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。静态方法与实例方法的区别1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例......
  • Java开发知识汇总
    1.目的:记录积累java开发知识,下次使用不用在发时间查找,好记性不如烂笔头。整理记录于2024.06.0923:25周日@程序员猴哥Java开发知识汇总.目录:==========01Java后端开发==========1.IDEA相关1IDEA安装2JDK的安装3Maven安装配置4Git的安装5IDEA全局......
  • 让你的网页动起来:Javascript+CSS拖曳盒子指南
    让网页上的元素具有拖拽功能,可以提升用户体验,尤其是在需要用户交互的场景中。下面是一个使用JavaScript和CSS来实现拖拽效果的基本指南。1.HTML结构首先,你需要在HTML中定义一个可拖拽的元素,通常是一个div。 <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"......
  • Java 18风暴来袭:解锁编程新纪元
    一、引言Java18的发布标志着Java语言在性能、安全性和开发效率方面的又一次飞跃。本次更新不仅带来了新的语言特性,还包括了一些实验性功能和工具的改进。这些新特性旨在帮助开发者编写更高效、更安全的代码,并提升开发体验。二、新特性概述1.默认使用UTF-8字符编码(JEP400)......
  • java基础
    java题目练习day01Java提供的四种访问控制符Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。default:在同一个包里面可见,不适用任何修饰符。使用对象:类、接口、变量、方法。private:在同一类中可见。使用对象:变量、方......
  • 【JavaScript】了解 Sass:现代 CSS 的强大预处理器
    我已经从你的全世界路过像一颗流星划过命运的天空很多话忍住了不能说出口珍藏在我的心中只留下一些回忆                     ......
  • 【华为OD】D卷真题100分:数组拼接 JavaScript代码实现[思路+代码]
    【华为OD】2024年C、D卷真题集:最新的真题集题库C/C++/Java/python/JavaScript【华为OD】2024年C、D卷真题集:最新的真题集题库C/C++/Java/python/JavaScript-CSDN博客JS、Java、C、C++、python代码实现:【华为OD】D卷真题100分:数组拼接JavaScript代码实现[思路+代码]-CSDN......
  • Javaweb01-HTTP协议
    HTTP协议1.HTTP协议概述定义与作用HTTP是超文本传输协议,用于定义服务端和客户端之间交换数据的过程和数据本身的的格式,同时HTTP是一种请求/响应式的协议,是一种应用层的协议优点支持客户端和服务器模式简单快速,通过Http请求和Http响应加快通信允许传输任意类型的数据,......