服务器Docker OOM RSS高问题排查思路

优质博文:IT-BLOG-CN

防走弯路为防止走弯路,强烈建议先仔细阅读以下加粗内容:
如果你的应用是因为公司最近降成本调小实例物理内存才出现docker oom,而之前从来没有出现过,那么大概率是堆内存太大导致,这种情况尤其在实例物理内存<=4G的情况下多发。所以如果你的case满足以下场景,基本是堆内存设置不合理导致:
【1】先看看你的应用heap实机用量,然后调小xmx到一个比较合理的数值,这里一定要自己定义xmx,不要用ops默认的计算值,因为那个计算值会把xmx设置为物理内存的3/4,对于小内存来说,这太大了;
【2】很多应用为了避免堆内存扩容导致性能下降,会将xmsxms设置成一样大,也会加剧小物理内存实例的docker oom。所以建议小内存实例把xms设置为物理内存的1/21/3
【3】如果有的groupoom(比如ALI集群有oom),有的一直没有,那么基本不是内存泄露导致,因为如果有内存泄露,那么任何集群都会出现oom。猜测这种情况因为实例底层参数差异导致(暂无法求证),建议你把oom集群堆内存调小256m看看oom是否得到解决;
【4】如果不满足上述两种情形,那就继续往下看吧;

一、前言

Java进程使用的内存分为3部分:堆内存、虚拟机所使用的内存(一般叫Native Memory)、堆外内存(off-heap)组成。
【1】堆heap内存也就是你jvm参数里面设置的xmxxms所指定的大小。如果你的工程里面的extraenv.sh没有指定xms/xms,那么ops会默认给你指定成物理内存的3/4。比如物理内存4G,那么堆内存会是3072m,这其实有点太大了;
【2】Native memory:虚拟机使用的内存,分为很多细分的区域,比如classgcthreadcodeinternal等;

名称作用描述
Java Heap你所使用的堆内存
Class元数据区,存储class信息,lambda表达式编译成的临时类也存放在这里面,可以通过-XX:MaxMetaspaceSize=256m限制大小
Thread线程使用的内存区域, 用量可以简单的换算关系为线程数*xss。默认xss=1024k。如果你有1000个线程,那么Thread区可以占到1G。所以一般我们会设置xss=256k
CodeJIT编译后的机器码所使用的区域
GC垃圾回收器使用的内存区域,比如card table、RSet等。性能越好的垃圾回收器所占用的内存越大,另外如果你使用G1同时内存缓存了太多数据,那么G1也会占用很多的内存,因为它会把对象之间的引用关系放在RSet里面。
CompilerJIT编译将class编译成机器码时所使用的临时区域
Internal包含命令行解析器使用的内存、JVMTI、PerfData 以及 Unsafe 分配的内存等
Other其他没有被覆盖到的区域
SymbolSymbol 为 JVM 中的符号表所使用的内存,HotSpot中符号表主要有两种:SymbolTable 与 StringTable(字符串常量池)
Native Memory TrackingNMT开启是有性能损耗的,这块区域就是JVM为Native Memory Tracking开辟的内存区域
Arena ChunkJVM 分配的一些 Chunk(内存块),当退出作用域或离开代码区域时,内存将从这些 Chunk 中释放出来。然后这些 Chunk 就可以在其他子系统中重用. 需要注意的是,此时统计的 Arena 与 Chunk ,是 HotSpot 自己定义的 Arena、Chunk,而不是 Glibc 中相关的 Arena 与 Chunk 的概念
LoggingJVM logging使用内存
ArgumentsMemory for arguments
ModuleMemory used by modules

服务器RSS过高会导致被拉出或者OOM,虽然各个公司现有的高可用机制能自动检测并通过进程重启的方式恢复服务,但我们需要知道RSS过高是什么原因导致的。本文会梳理一下RSS过高的排查套路,避免你走弯路。

首先,我们需要知道的是,Java进程消耗的内存绝不仅仅是你设置的Xmx这么简单,Java进程占用的内存分为3部分:堆内存、JVM自身消耗的内存、堆外内存。所以,后续的排查思路我们也是按照上面的3个方面来顺序展开。下图是各种JVM垃圾回收器消耗内存的比例,注意这部分内存是堆内存之外的:
在这里插入图片描述

实践证明,G1内存开销甚至能占到堆内存的20%,恐怖吧,惊喜不……

1 确认堆内存是否太大
首先要确认一下Java应用的堆内存是否太大,这个可以通过hickwall的appid docker linux tomcat多机模板 VM 查看。因为JVM自身也会消耗一些内存,所以你至少需要预留出部分内存存给JVM使用。如果应用涉及较多的网络通信,那还需要预留一些内存给堆外使用,所以一般来说你的堆内存最多为服务器物理内存减去1.2G,如4G服务器,那么堆内存最大为2.8G;(之前为3G,但就目前的趋势看,jvm自己用的内存也越来越多,所以这里调整成2.8G。当然这里是经验值,还是要结合你的应用所使用的堆内存决定)。
如果确认你的堆内存使用过多的话,那么问题相对简单了,我们可以通过dump堆内存,使用mat分析基本能定位到问题。这方面的问题很多,大家可以自行搜索。
2 确认是否DirectByteBuffer用量太大
我们知道,很多有网络交互的组件底层是基于netty或者nio的,而nio为了提高性能,会申请一部分堆外内存,有的是基于DirectByteBuffer申请的,如果网络交互频繁而且报文尺寸大,这部分内存的占用还是比较可观的,比如下面这个应用,DirectByteBuffer用量都能达到700m。

在这里插入图片描述

需要注意的是:Java对于DirectByteBuffer的用量是没有做限制的,也就是它可以申请物理内存那么多的用量。所以如果你的DirectByteBuffer用量比较多,可以通过在jvm参数中设置-XX:MaxDirectMemorySize这个参数控制一下。在DirectByteBuffer用量超过你设置的大小时,系统会触发一次System.gc()(未必是FGC)。
3 确认是否存在大量ARENA区
如果堆内存不大,那么继续排查非堆内存。首先我们去看一下ARENA区。在高并发的应用中,往往ARENA区占用的内存会比较多。至于ARENA是什么东东,可以读一下这篇文章:https://blog.csdn.net/maokelong95/article/details/51989081
执行如下命令:

sudo -u deploy pmap -x <pid>|sort -gr -k2 |less 

如果存在大量大小为6553665404的内存区域,则说明ARENA区域占用了太多的内存,如下图所示:
在这里插入图片描述

这种情况下,有2个选择:内存分配器换成jemalloc,详情见:jemalloc 服务 ( http://conf.ctripcorp.com/pages/viewpage.action?pageId=1131500186 )粗暴的办法是修改环境变量的值,在extraenv.sh中添加这么一行:export MALLOC_ARENA_MAX=1需要注意的是,上述的数值只能是1,其他大于1的数值经实践证明是无法控制ARENA数量的。4 确认Native Memory开销过大如果前面2个步骤看到的内存开销都不大,还有很多内存区域你不知道消耗在哪里了,那么我们开始第三步:开启Native Memory Tracking。前面我们说过,Java应用的执行,JVM自身也需要消耗一些内存的,通过开启Native Memory Tracking,我们就能知道JVM自身消耗了多少内存。请不要迷信vi里面的非堆内存消耗,那是不准确的……书归正传,通过修改/opt/tomcat/bin/setenv.sh中的JVM参数并重启Java进程开启:

-XX:NativeMemoryTracking=detail 

进程重启后,可以通过以下命令查看NativeMemory的占用情况:

sudo -Esu deploy /usr/java/default/bin/jcmd <pid> VM.native_memory summary 

你会看到以下类似的执行结果:
在这里插入图片描述

通过上图,你可以看到JVM各个区域所使用的内存大小,主要包含了Java Heap、Class、Thread、Code、GC、Compiler、Internal、Other、Symbol、Native Memory Tracking、Arena Chunk这几部分。
需要注意的是Class、Thread、GC几个区域的大小。
上面的图片是27G的堆内存,使用G1回收器,你能看到GC区居然占用了3.8G的内存。
针对上面的各个区域大小做加法,看一下是否接近于RSS的大小,如果是,恭喜你可以到此结束了。后续你需要做的就是针对内存占用比较大的JVM区去做优化,这里就不详细介绍了。
如果不是,很遗憾,你进入到了最难啃的环节,继续往下看吧。

需要注意的是,开启NativeMemoryTracking会造成5%的性能下降,用完记得关闭。
可以通过以下命令临时关闭:

sudo -Esu deploy /usr/java/default/bin/jcmd <pid> VM.native_memory stop 

或者修改/opt/tomcat/bin/setenv.sh中的JVM参数并重启永久关闭。

如果你嫌上面的文章安装配置比较麻烦,你也可以用我的jtoolkit去排查,安装命令很简单:

curl -L http://devresources.flight.ctripcorp.com/jtoolkit/shells/install.sh|bash 

安装完毕,根据提示去使用就可以了,希望通过上面的介绍,能解决你的问题。

如果很不幸你的问题还没有得到解决的话,那么抱歉,这超出了我的认知了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/61436.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop的汽车销量数据分析系统

摘要 随着大数据技术的快速发展&#xff0c;基于大数据的分析应用日益普及。本论文针对汽车行业&#xff0c;本论文设计并实现了一个基于Hadoop的汽车销量数据分析系统&#xff0c;旨在提供汽车行业的决策支持和市场洞察。系统首先通过网络爬虫和API等方式采集汽车销量数据&am…

在应用启动时,使用 UniApp 提供的 API 检查和请求权限。

在使用 UniApp 开发 App 时&#xff0c;如果你需要在应用启动时提示用户获取本地媒体权限&#xff0c;可以按照以下步骤操作&#xff1a; 1. 明确需要的权限 要访问本地媒体&#xff08;如相机、麦克风或文件存储&#xff09;&#xff0c;需要申请以下权限&#xff1a; Andr…

掌握Git分布式版本控制工具:从基础到实践

一、引言 在软件开发过程中&#xff0c;版本控制是不可或缺的一环。Git作为一种分布式版本控制工具&#xff0c;以其高效、灵活的特点&#xff0c;受到了广大开发者的青睐。本文将详细介绍Git的基本概念、工作流程、常用命令&#xff0c;以及在IntelliJ IDEA中的操作方法。 二、…

【Python】FastAPI:Token认证

FastAPI&#xff1a;Token认证 本教程通过 FastAPI 实现用户登录和基于 JWT&#xff08;JSON Web Token&#xff09; 的认证与授权&#xff0c;适合初学者到进阶用户。教程特别关注 Depends、OAuth2PasswordBearer 等非基础操作的详细讲解&#xff0c;帮助你全面掌握相关技术。…

第7章硬件测试-7.3 功能测试

7.3 功能测试 7.3.1 整机规格测试7.3.2 整机试装测试7.3.3 DFX测试 功能测试包括整机规格、整机试装和整机功能测试&#xff0c;是整机结构和业务相关的测试。 7.3.1 整机规格测试 整机规格测试包括尺寸、重量、温度、功耗等数据。这些测试数据与设计规格进行比对和校验&…

芯片之殇——“零日漏洞”(文后附高通64款存在漏洞的芯片型号)

芯片之殇——“零日漏洞”(文后附高通64款存在漏洞的芯片型号) 本期是平台君和您分享的第113期内容 前一段时间,高通公司(Qualcomm)发布安全警告称,提供的60多款芯片潜在严重的“零日漏洞”,芯片安全再一次暴露在大众视野。 那什么是“零日漏洞”?平台君从网上找了一段…

「Mac玩转仓颉内测版23」基础篇3 - 深入理解整数类型

本篇将详细讲解Cangjie中的整数类型&#xff0c;探讨整数的定义、操作、表示范围、进制表示、类型转换及应用场景&#xff0c;帮助开发者在Cangjie中灵活运用整数类型构建程序逻辑。 关键词 有符号整数与无符号整数表示范围与溢出进制表示类型转换字面量与操作 一、整数类型概…

如何恢復電腦IP地址的手動設置?

手動設置IP地址後&#xff0c;可能會遇到一些網路連接問題&#xff0c;或者需要恢復到之前的自動獲取狀態。這篇文章將詳細介紹如何恢復電腦的IP地址設置。 為什麼需要恢復IP地址設置&#xff1f; 網路連接問題&#xff1a;手動設置IP地址後&#xff0c;可能會導致與路由器或…

Python-简单病毒程序合集(一)

前言&#xff1a;简单又有趣的Python恶搞代码&#xff0c;往往能给我们枯燥无味的生活带来一点乐趣&#xff0c;激发我们对编程的最原始的热爱。那么话不多说&#xff0c;我们直接开始今天的编程之路。 编程思路&#xff1a;本次我们将会用到os,paltform,threading,ctypes,sys,…

跟我学C++中级篇——Design Patterns的通俗说法

一、设计模式 Design patterns&#xff0c;软件设计模式&#xff0c;它是什么&#xff1f;很多初学者会被这种高大上的东西给唬住。其实&#xff0c;所有的书籍上都说得很清楚&#xff0c;只是它们把这种说法说得很高大上而已。举个简单例子&#xff0c;在抖音上经常可以看到介…

Linux16 多路复用(二)

多路复用&#xff08;二&#xff09; 1. 多路复用 - poll功能函数原型关于 pollfd 结构体类型poll 代码框架一些示例代码poll相较于select优点poll的缺点 2. 多路复用 - epoll关于epoll的三个系统调用创建 - epoll_create控制 - epoll_ctl函数原型关于 epoll_event 结构体 等待…

数字化那点事:一文读懂物联网

一、物联网是什么&#xff1f; 物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;是指通过网络将各种物理设备连接起来&#xff0c;使它们可以互相通信并进行数据交换的技术系统。通过在物理对象中嵌入传感器、处理器、通信模块等硬件&#xff0c;IoT将“…

Jmeter的后置处理器(二)

5--JSR223 PostProcessor 功能特点 自定义后处理逻辑&#xff1a;使用脚本语言编写自定义的后处理逻辑。支持多种脚本语言&#xff1a;支持 Groovy、JavaScript、BeanShell 等脚本语言。动态参数传递&#xff1a;将提取的数据存储为变量&#xff0c;供后续请求使用。灵活性高…

非对称之美(贪心)

非对称之美(贪心) import java.util.*; public class Main{public static void main(String[] arg) {Scanner in new Scanner(System.in);char[] ch in.next().toCharArray(); int n ch.length; int flag 1;for(int i 1; i < n; i) {if(ch[i] ! ch[0]) {flag …

(11)(2.2) BLHeli32 and BLHeli_S ESCs(一)

文章目录 前言 1 参数说明 前言 BLHeli 固件和配置应用程序的开发是为了允许配置 ESC 并提供额外功能。带有此固件的 ESC 允许配置定时、电机方向、LED、电机驱动频率等。在尝试使用 BLHeli 之前&#xff0c;请按照 DShot 设置说明进行操作(DShot setup instructions)。 根据…

初始化列表和在构造函数体内赋值有什么区别?

1. 成员初始化方式 初始化列表&#xff1a; 在构造函数参数列表后面&#xff0c;使用冒号 : 进行初始化&#xff0c;成员变量在对象创建时直接初始化。语法&#xff1a;ClassName(Type1 arg1, Type2 arg2) : member1(arg1), member2(arg2) { }对于基本类型&#xff0c;使用初始…

Redis持久化、主从及哨兵架构详解

Redis持久化 RDB快照&#xff08;snapshot&#xff09; 在默认情况下&#xff0c;Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。 你可以对Redis进行设置&#xff0c;让它在“N秒内数据集至少有M个改动”这一条件被满足时&#xff0c;自动保存一次数据集。 比…

【Linux庖丁解牛】—Linux基本指令(下)!

目录 1、grep指令 2、zip/unzip指令 3、sz/rz指令 4、tar指令 ​编辑 5、scp指令 6、bc指令 7、uname –r指令 8、重要的几个热键 9、关机 10、完结撒花 1、grep指令 grep是文本过滤器&#xff0c;其作用是在指定的文件中过滤出包含你指定字符串的内容&#xff0c;…

Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行

问题 IOS14设备&#xff0c;切后台划掉&#xff0c;二次启动崩溃。 原因 IOS14以上 flutter 不支持debugger模式下的二次启动 。 要二次启动需要以release方式编译工程安装至手机。 操作步骤 清理项目&#xff1a;在命令行中运行flutter clean来清理之前的构建文件。重新构…

十六:请求与响应的上下文

在Web开发中,请求与响应是数据流动的核心。客户端发出请求,服务器返回响应,这一过程看似简单,但背后涉及复杂的上下文管理。理解并优化Web请求与响应的上下文,不仅能提升系统性能,还能改善用户体验。 请求与响应的基础 Web请求是由客户端(通常是浏览器或移动应用)向服…