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

打印 上一主题 下一主题

主题 919|帖子 919|积分 2757

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

Java进程使用的内存分为3部分:堆内存、虚拟机所使用的内存(一样平常叫Native Memory)、堆外内存(off-heap)构成。
【1】堆heap内存也就是你jvm参数内里设置的xmx和xms所指定的大小。如果你的工程内里的extraenv.sh没有指定xms/xms,那么ops会默认给你指定成物理内存的3/4。好比物理内存4G,那么堆内存会是3072m,这实在有点太大了;
【2】Native memory:虚拟机使用的内存,分为很多细分的区域,好比class、gc、thread、code、internal等;
名称作用描述Java Heap你所使用的堆内存Class元数据区,存储class信息,lambda表达式编译成的临时类也存放在这内里,可以通过-XX:MaxMetaspaceSize=256m限制大小Thread线程使用的内存区域, 用量可以简朴的换算关系为线程数*xss。默认xss=1024k。如果你有1000个线程,那么Thread区可以占到1G。所以一样平常我们会设置xss=256kCodeJIT编译后的机器码所使用的区域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 argumentsModuleMemory 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
执行如下命令:
  1. sudo -u deploy pmap -x <pid>|sort -gr -k2 |less
复制代码
如果存在大量大小为65536或65404的内存区域,则说明ARENA区域占用了太多的内存,如下图所示:

这种环境下,有2个选择:内存分配器换成jemalloc,jemalloc 服务粗暴的办法是修改环境变量的值,在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进程开启:
  1. -XX:NativeMemoryTracking=detail
复制代码
进程重启后,可以通过以下命令查察NativeMemory的占用环境:
  1. 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%的性能下降,用完记得关闭。
可以通过以下命令临时关闭:
  1. sudo -Esu deploy /usr/java/default/bin/jcmd <pid> VM.native_memory stop
复制代码
或者修改/opt/tomcat/bin/setenv.sh中的JVM参数并重启永世关闭。
如果你嫌上面的文章安装设置比较麻烦,你也可以用我的jtoolkit去排查,安装命令很简朴:
  1. curl -L http://devresources.flight.ctripcorp.com/jtoolkit/shells/install.sh|bash
复制代码
安装完毕,根据提示去使用就可以了,渴望通过上面的先容,能解决你的问题。
如果很不幸你的问题还没有得到解决的话,那么歉仄,这超出了我的认知了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表