JVM调优实战--容器内应用内存溢出被杀死

2023-11-15

威哥评:这篇文章是我的小伙伴hwang根据他在production环境遇到的实际问题整理的,简单来说就是让JVM听你的话,说内存多大就多大。说实话,hwang这篇文章给我了一个小小的惊喜,他的文笔真是不错,很生动的技术文章。

所谓的容器应用内存溢出被杀死,即为oom(out of memory) killed exit code 137。

初次遭遇
相信玩java容器化部署的朋友肯定遇到过oom killed exit code 137,解决方案很多,糙快猛的就重启或者直接docker run --restart=always。去年第一次遇到的时候,哈哈,是秀一下自己还知道点jvm调优的时候了:

docker run … JAVA_OPTS=“-server -Xmx1024m”
顺道还长了点其它知识,

docker run … --memory1300MJAVA_OPTS=“-server -Xmx1024m”
容器的内存要大于Xmx内存,此前有不知道的同学配置失误导致启动失败,宣导之。以为从此可以高枕无忧了。

服务又挂了
岂料,好景仅半年,近日频遭下游业务方投诉,你们的服务又双叒叕不能用了!去现场扫了一眼,oom killed exit code 137。太好了,又可以jvm调优了。于是再给配上heapdump

docker run … --memory1300MJAVA_OPTS=“-server -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/”
服务很配合的一两天就又挂了。再去现场,咦,我的heapdump呢?为什么日志文件夹下除了日志啥都没有呢?xxx.hprof呢?此时,以为这是一个传统的JVM调优问题,通过heapdump揪出问题代码即可。好,开始解决为啥没有生成heapdump的问题,认真的google了半天,但是网上似乎没有朋友遇到类似的问题,配置大同小异,为啥人家能dump出来。试试百度……还是算了。晚上没招了,决定换个思路,还是顺藤摸瓜,搜搜oom killed exit code 137试试吧,不想还真是搜出来点东西。

为什么会OOM Killed?
​首先,交代点背景,线上容器的jdk版本是1.8.0_171。原来,在jdk1.8的早期版本时候,docker还没出生呢,所以对于java应用来说,其实它并不知道自己是在物理机上还是在容器里面,身在福中不知福啊。这会导致一个问题,如果不加Xmx配置,那么jvm默认会最大获取物理机内存的四分之一;而如果配置了呢,一般因为java容器主要只跑单个java应用,那么容器的最大内存和xmx内存差距比较小,尽管前者较大,由于堆外内存/metaspace的存在,导致java应用仍然会在负载较大的时候超出容器内存,导致容器oom killed,但这个时候对于JVM的认知来说,仍然认为自己ok的,还能玩呢,自然不会heapdump,就这么安乐死了。不废话,怎么让jvm意识到自己在容器里呢?

解决方案:

docker run … --memory 1300M
JAVA_OPTS=“-server -XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/”
可以看到,这次多了三个参数,少了Xmx(不能再用了,会覆盖该三个参数)-

​-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap-XX:MaxRAMFraction=2
前两个参数告诉JVM,你身在何处。MaxRAMFraction控制最大堆内存占容器内存的比例,即容器内存/MaxRAMFraction,只能取整数。好在oracle把这些参数支持backport到jdk1.8_171上了,太好了,不用升级了。不过,还不完美,如果MaxRAMFraction取1,JVM Xmx接近容器最大内存,很容易被oom killed。而如果取2或者更大,则xmx又太小,或者容器内存要给很大才能让xmx满足需求,但是这样会浪费物理内存,在鄙司要点物理资源可是很不容易的。好在jdk1.8_191以上版本加入了百分比参数,可以精确控制,太好了,还是得升级。

docker run … --memory 1300M
JAVA_OPTS=“-server
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:MaxRAMPercentage=80.0
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/logs/”
这次从MaxRAMFraction变成了MaxRAMPercentage,完美,不过记住必须用double型,用整型竟然会报错启动不了。此时经测试,可以正常生成heapdump文件。

JVM配置+Xmx为啥仍然会多用内存?
1.不同配置下JVM的消耗情况

  1. 容器内存100M,xmx不限,此时xmx最大会用到物理机1/4,远远超过容器内存限制。

docker run-m100MB-it–rm image:jdk1.8.0_171 java-XshowSettings:vm-version

  1. 容器内存100M, xmx=80M:

docker run-m 100MB -it --rm image:jdk1.8.0_171 java -Xmx80m -XshowSettings:vm-version

  1. 容器内存100M, 增加容器感知,自行计算Xmx,且不会超过容器限制。

docker run -m 100MB -it --rm image:jdk1.8.0_171 java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XshowSettings:vm -version

2.JVM的内存消耗分布
总内存 = Heap + Code Cache + Metaspace + Symbol tables + Other JVM structures + Thread stacks + Direct buffers + Mapped files + Native Libraries + Malloc overhead + …

可见,除了堆内存之外,还有其它堆外内存消耗。二者加总,可能超过容器限制。

3.另一种解决方案
-XX:MaxRAM=1g-XX:MaxRAMFraction=2

可以使用MaxRAM限制java应用整体内存消耗:堆内存+堆外,但是问题是MaxRAMFraction控制不够精确,还是会造成内存浪费,所以作为次优方案。

结语
综上,容器oom killed,跟一般传统的java.lang.OutOfMemoryError异常是两码事。java.lang.OutOfMemoryError发生是因为堆内存不够,此时需要增加Xmx。而容器oom killed,是因为堆外内存+堆内存总体超出限制而导致,是容器行为,所以不会产生heapdump。

参考
OOM Killer and Java applications in containers
​medium.com/logistimo-engineering-blog/oom-killer-and-java-applications-c0dfd7f6b036

Analyzing java memory usage in a Docker container
​trustmeiamadeveloper.com/2016/03/18/where-is-my-memory-java/

Docker and Java: Why My App Is OOMKilled
​dzone.com/articles/why-my-java-application-is-oomkilled
Running a JVM in a Container Without Getting Killed
​blog.csanchez.org/2017/05/31/running-a-jvm-in-a-container-without-getting-killed/
Docker support in Java 8 — finally!
​blog.softwaremill.com/docker-support-in-new-java-8-finally-fd595df0ca54
Java using much more memory than heap size (or size correctly Docker memory limit)
​stackoverflow.com/questions/53451103/java-using-much-more-memory-than-heap-size-or-size-correctly-docker-memory-limi
OpenJDK and Containers
​developers.redhat.com/blog/2017/04/04/openjdk-and-containers/#more-433899
JVM Memory Settings in a Container Environment
​medium.com/adorsys/jvm-memory-settings-in-a-contain

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM调优实战--容器内应用内存溢出被杀死 的相关文章

随机推荐

  • Mac平台安卓模拟器:网易MuMu mac中文免费版(支持12系统)

    网易MuMu Mac版是一款可以让Mac用户在电脑上轻松玩手游的安卓模拟器 是迄今为止国内最好最流畅的手游模拟器软件 网易mumu mac版现已支持梦幻西游 大话西游 倩女幽魂等众多经典安卓手机游戏 mumu模拟器mac版为大家提供海量免费
  • Python模块-pandas

    目录 数据读取 数据探索 数据清洗 数据清洗 类型转换 缺失值 重复值 值替换 修改表结构 新增列 删除列 删除行 修改列名 数据分组 数值变量 数据分列 分类变量 设置索引 排序 数据筛选 切片 多表拼接 数据聚合 分组运算 groupb
  • 高数:第一章:函数、极限、连续

    文章目录 一 函数 1 函数的概念 基本初等函数 初等函数 2 函数的性质 函数四性态 1 单调性 2 奇偶性 3 导函数的奇偶性 3 周期性 4 有界性 5 对称性 3 基本不等式 4 开根要带绝对值 二 极限 1 极限的概念 数列极限
  • R语言的基础语法及常用命令

    R其实对于数据分析来说只是工具而已 所以刚开始不需要学习多么深多么细 只需要能够满足当前需求就行 之后的在实践中慢慢学习 毕竟想要把R学精并不是容易的事情 正确的做法就是边做边学 不会就google翻文档 本片主要是R的基础语法及常用的命令
  • 关系运算和逻辑运算( &与&& 和

    关系运算和逻辑运算 关系运算 比较 gt gt lt lt 对象 instanceof 类 1 区分 和 区别 赋值符号 将 后面的结果 值 引用 存入 左边的变量空间内 比较符号 比较 后面的元素 值 引用 与前面的是否一致 2 比较运算
  • 外部组件发生异常怎么解决_火绒提示安全服务异常是怎么回事?三种方法帮你轻松解决此问题...

    平时我们在使用电脑的过程当中 为了保护电脑的安全 我们往往会在电脑上安装防护类的软件 比如火绒安全软件 它是一款集杀防于一体的电脑防御及杀毒类安全软件 使用这款软件来保护电脑 不仅软件的体积小巧 占用资源小 而且它的功能强大 可以对我们的电
  • 如何向这个public static void main(String[] args)中的args数组传递参数呢

    如何向这个public static void main String args 中的args数组传递参数呢 重新认识 main 方法 要向 public static void main String args 中的 args 数组传递参
  • Jquery中each的三种遍历方法

    1 选择器 遍历 div each function i i就是索引值 this 表示获取遍历每一个dom对象 2 选择器 遍历 div each function index domEle index就是索引值 domEle 表示获取遍历
  • python 蓝桥 数列排序

    题目 数列排序 问题描述 给定一个长度为n的数列 将这个数列按从小到大的顺序排列 1 lt n lt 200 原因分析 输出格式 输出一行 按从小到大的顺序输出排序后的数列 样例输入 5 8 3 6 4 9 样例输出 3 4 6 8 9 解
  • LittleFOC工程简记——基于定点数的电流PI控制器设计

    LittleFOC工程简记 基于定点数的电流PI控制器设计 这里罗列了系列文章链接 文章目录 LittleFOC工程简记 基于定点数的电流PI控制器设计 前言 电机系统 工程分析 工程代码 前言 在FOC程序在设计的过程中 对于很多芯片而言
  • 地面分割--Patchwork

    文章目录 1问题定义 2同心区域模型 3按照区域划分的平面拟合 4地面点似然估计 GLE 总结 patchwork是一种比较优秀的地面分割方法 其过程主要分为三个部分 同心圆环区域 CZM concentric Zone Model 按照区
  • Qt connect 第五个参数

    一 Qt connect 函数原型如下 第五个 5种 参数根据接收者和发送者是否在同一个线程不同 QObject connect const QObject sender const char signal const QObject re
  • 【安全与协议】使用crypto.js进行加密详解

    JavaScript Crypto JS 前言与工具 前言 使用 Crypto JS 可以非常方便地在 JavaScript 进行 MD5 SHA1 SHA2 SHA3 RIPEMD 160 哈希散列 进行 AES DES Rabbit R
  • chatgpt赋能python:Python中如何取出列表中的数字

    Python中如何取出列表中的数字 在Python编程中 经常需要从一个包含数字和其他类型数据的列表中仅取出数字元素 这可以通过几种不同的方法来实现 下面将介绍其中常用的几种方法 1 使用循环遍历 第一种方法是使用循环遍历列表 并检查每个元
  • Android Gradle 插件版本说明

    Android Gradle 插件版本说明 在更新 Android Studio 时 您可能会收到一并将 Gradle 更新为最新可用版本的提示 您可以选择接受该更新 也可以根据项目的构建要求手动指定版本 下表列出了各个 Android G
  • uniApp获取元素信息

    uniApp获取元素信息的代码 详细了解请查阅文档 uni createSelectorQuery const query uni createSelectorQuery in this query select press boundin
  • UE 材质学习

    值材质三原素 材质 材料 肌理 纹络 or 纹理 图案 Material Texture Pattern UE5中对应材质的 三原素 的内容 材质 Metallic 金属感 Roughness 粗糙度 Specular 高光 镜面 肌理 N
  • Oulipo 【HDU - 1686】【哈希

    题目链接 求模式串在待匹配串的出现次数 Input 第一行是一个数字T 表明测试数据组数 之后每组数据都有两行 第一行为模式串 长度不大于10000 第二行为待匹配串 长度不大于1000000 所有字符串只由大写字母组成 Output 每组
  • 202326读书笔记

    202326读书笔记 读给孩子的时令古词 冰肌绰约月朦胧 仿佛暗香浮动 竹杖芒鞋轻胜马 谁怕 一蓑烟雨任平生 料峭春风吹酒醒 微冷 山头斜照却相迎 春 雨水 惊蛰 春分 清明 谷雨 夏 小满 芒种 小暑 大暑 秋 处暑 白露 寒露 霜降 冬
  • JVM调优实战--容器内应用内存溢出被杀死

    威哥评 这篇文章是我的小伙伴hwang根据他在production环境遇到的实际问题整理的 简单来说就是让JVM听你的话 说内存多大就多大 说实话 hwang这篇文章给我了一个小小的惊喜 他的文笔真是不错 很生动的技术文章 所谓的容器应用内