Out of memory (OOM) 是一种操作系统或者程序已经无法再申请到内存的状态。经常是因为所有可用的内存,包括磁盘交换空间都已经被分配了。OOM的官方解释是:Understand the OutOfMemoryError Exception,根据HeapDump性能社区专属讲师公与的总结,常见的OOM有以下10种(其中OOM Killer是操作系统层面的概念)。
那些年我们遇到的OOM-开题
开篇推荐的是公与大佬的视频课程《那些年我们遇到的OOM》第一期,在本期课程中,公与重新梳理了大众对于OOM的理解和认知,简单而清晰地介绍了Java虚拟机内存分布,并对本系列课程中出现的10种OOM进行了简单的解读,观看此视频课程能让大家对OOM有一个整体的认识。
不同类型的OOM产生原因是什么?生产环境碰到OOM该如何分析、怎么解决?有哪些好的定位工具?
本期HeapDump性能社区OOM专题文章就收录了20篇相关文章,包含专家经验总结、大佬源码分析,以及大量的实战案例。希望大家读完都有收获。
第一部分:理论篇
1.JVM 发生 OOM 的 8 种原因、及解决办法
作者:占小狼
本文简单介绍了8种OOM的产生原因及其解决方法,包括分析方法、排查命令、参数调优思路等。
2.一篇说明白什么是oom,为什么会oom,oom的类型和常见解决方法
作者:G.Shine
本篇最早来自博客园G.Shine 2012年的一篇文章,是关于OOM问题总结得最简单,也最易懂的一篇。
涉及到的虚拟机的技术或者工具,往往需要考虑到虚拟机规范以及不同的虚拟机实现。尤其是针对虚拟机调优时,往往需要针对虚拟机在某些方面的实现策略来考虑,比如,不同的虚拟机的垃圾回收算法是不一样的,而这直接影响了虚拟机某些参数的设置,以达到虚拟机的最佳性能。而针对JVM运行时的分析与诊断,则需要掌握分析基本方法,针对具体情况,运用虚拟机的原理,具体分析,可以多看看HeapDump性能社区里JVM相关的文章,也可以使用社区的XXFOX工具:https://opts.console.heapdump.cn/
3.JVM源码分析之临门一脚的OutOfMemoryError完全解读
作者:你假笨
OutOfMemoryError,说的是java.lang.OutOfMemoryError,是JDK里自带的异常,顾名思义,说的就是内存溢出,当我们的系统内存严重不足的时候就会抛出这个异常(PS:注意这是一个Error,不是一个Exception,所以当我们要catch异常的时候要注意哦),这个异常说常见也常见,说不常见其实也见得不多,不过作为Java程序员至少应该都听过吧,如果你对jvm不是很熟,或者对OutOfMemoryError这个异常了解不是很深的话,这篇文章肯定还是可以给你带来一些惊喜的,通过这篇文章你至少可以了解到如下几点:
- OutOfMemoryError一定会被加载吗
- 什么时候抛出OutOfMemoryError
- 会创建无数OutOfMemoryError实例吗
- 为什么大部分OutOfMemoryError异常是无堆栈的
- 我们如何去分析这样的异常
4.Java中利用软引用和弱引用来避免oom
作者:Matrix海 子
本文先介绍了有关强引用、软引用、弱引用、虚引用的相关概念,然后举例讲解如何利用软引用和弱引用来避免OOM:假如有一个应用需要读取大量的本地图片,如果每次读取图片都从硬盘读取,则会严重影响性能,但是如果全部加载到内存当中,又有可能造成内存溢出,此时使用软引用可以解决这个问题。设计思路是:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。在Android开发中对于大量图片下载会经常用到。
5.OOM系列之八:java.lang.OutOfMemoryError: Kill process or sacrifice child
此文来自于plumbr官网,plumbr是一个常用的JVM 监测工具,官网有完整的oom和gc文章,本人仅作翻译转载,此篇为完结篇。
第二部分:实战篇
6.体验了一把线上CPU100%及应用OOM的排查和解决过程
作者:阿飞云
作者在收到应用异常告警后,登录了出现问题的服务器进行检查,在查看服务的日志时发现服务OOM了,紧接着使用Top命令查看系统中各个进程的资源占用状况,发现有一个进程CPU使用率达到了300%,然后查询该进程下所有线程的CPU使用情况并保存堆栈数据。根据前述操作,获取了出现问题的服务的GC信息、线程堆栈、堆快照等数据之后,使用HeapDump社区提供的XElephant进行分析,发现是InMemoryReporterMetrics引起的OOM,进一步发现出现问题的这个服务依赖的zipkin版本较低,将其升级后解决了问题。本文描述和解决的虽然不是罕见的疑难杂症,但排查思路清晰,过程完整,还推荐了排查工具,适合新手阅读学习。
7.一次容器化springboot程序OOM问题探险
作者:侠梦
作者被告知一个容器化的java程序每跑一段时间就会出现OOM问题,首先查日志并未发现异常;然后通过JStat查看GC情况,发现GC情况正常但ByteBuffer对象占用最高(异常点1);接着通过JStack查看线程快照情况,发现创建了过多kafka生产者(异常点2);最后通过编写Demo程序验证猜想,确定问题是业务代码中循环创建Producer对象导致的。排查过程清晰明了,工具使用娴熟,验证过程快速准确。
8.一次真实的线上OOM问题定位
作者:王政
笔者负责的一系统生产环境上出现了OutOfMemoryError,伴随着这个问题随之而来的是一堆Full GC,CPU 百分之百,频繁宕机重启等问题,严重影响业务的推广及使用,此类问题一般处理起来比较棘手,本文将此问题的出现及定位解决过程做下梳理,以便对后续处理类似问题提供参考指导。
9.一次百万长连接压测 Nginx OOM 的问题排查分析
作者:挖坑的张师傅
作者在一次百万长连接压测中,发现32C 128G的四台Nginx频繁出现OOM。发现问题后首先查看了 Nginx 和客户端两端的网络连接状态,首先怀疑是jmeter客户端处理能力有限,有较多消息堆积在中转的Nginx处,于是dump了nginx的内存查看,坚定了是因为缓存了大量的消息导致的内存上涨;随后查看了 Nginx 的参数配置,发现proxy\_buffers 这个值设置的特别大;然后模拟了upstream 上下游收发速度不一致对Nginx内存占用的影响。最后将proxy\_buffering 设置为 off并调小了 proxy\_buffer\_size 的值以后,Nginx的内存稳定了。作者排查思路清晰,工具使用、参数调节十分娴熟,对底层原理和源码理解深刻,无论是经验还是态度都十分值得学习参考。
10.一则OOM死机故障的处理过程
作者:兔兔七
笔者这次遇到的故障属于典型的内存不足,导致发生OOM错误的情况。从系统状态和日常的监控以及系统日志的报错等都可发现踪迹,但缺没有引起足够的重视,最终导致死机。另外,mrtg运行出错,而发送大量垃圾邮件,引起amavisd调用clamscan被卡的问题也使系统不堪负载。类似的问题,需要针对系统进行监控以分析原因才能采取有效的措施。同时,管理员也不能忽视日常的维护工作,以免出现无可挽救的故障。
11.为什么容器内存占用居高不下,频频 OOM
作者:煎鱼
作者的项目在上 Kubernetes 的前半年,只是用 Kubernetes,开发没有权限,业务服务极少,忙着写新业务,风平浪静。在上 Kubernetes 的后半年,业务服务较少,偶尔会阶段性被运维唤醒,问之 “为什么你们的服务内存占用这么高,赶紧查”。此时大家还在为新业务冲刺,猜测也许是业务代码问题,但没有调整代码去尝试解决。在上 Kubernetes 的第二年,业务服务逐渐增多,普遍增加了容器限额 Limits,出现了好几个业务服务是内存小怪兽,因此如果不限制的话,服务过度占用会导致驱逐,因此反馈语也就变成了:“为什么你们的服务内存占用这么高,老被 OOM Kill,赶紧查”。经过一段跨度较长的时间的排查:怀疑业务代码(PProf)——怀疑其它代码(PProf)——怀疑 Go Runtime ——怀疑工具——怀疑环境,最终找出了原因并给出了解决方案。
12.为什么容器内存占用居高不下,频频 OOM(续)
作者:煎鱼
经过内部讨论,由于种种原因(例如:Linux、Kubernetes 太低),我们选择了升级 Linux 版本,也就是 CentOS 8,这样子其内核版本就会到达至 4.x(cgroup 已经健壮了许多,且在 4.5 cgroup v2 已经 GA),相关问题已经修复,并同步设置 cgroup.memory=nokmem 即可解决/避免相关问题。而在写下这篇文章时,我们可以看到 kmem accounting 的不少问题都已经被修复或提上日程,这对本次排查提供了相当大的便利,在确定问题的所在后根据 cgroup leak 沿着排查下去,基本都能看到大量的前人所经历过的 “挣扎”,大家若有兴趣,也可以跟着参考所提供的的链接做更一进步的深入了解。但事实上,不管哪个 Linux 内核版本,都存在着或多或少的问题,需要做好适当的心理准备,否则就会遇到 “没上容器时好好的” 的窘境,查起问题更麻烦。
13.刨根问底——记一次 OOM 试验造成的电脑雪崩引发的思考
作者:码海
作者通过一个 OOM 试验引出了三个值得思考的问题:while (true) 与 cpu 负载的关系?发生 OOM 后 Ctrl+C 为啥无法中止 Java 进程?主线程发生 OOM 后 Java 进程为啥不会停止运行?然后一一作了验证,并提醒读者看到书中的 demo 时最好能亲自去尝试一下,说不定能有新的发现!纸上得来终觉浅,绝知此事要躬行。碰到问题最好穷追猛打,这样我们才会收获很多,进步很快!
14.震惊!线上四台机器同一时间全部 OOM,到底发生了什么?
作者:码海
本文通过线上四台机器同时 OOM 的现象,来详细剖析定位了产生问题的原因,可以看到我们在应用某个库时首先要对这个库要有充分的了解(上述 HttpClient 的创建不用单例显然是个问题),其次必要的网络知识还是需要的,所以要成为一个合格的程序员,不光对语言本身有所了解,还要对网络,数据库等也要有所涉猎,这些对排查问题以及性能调优等会有非常大的帮助,再次,完善的监控非常重要,通过触发某个阈值提前告警,可以将问题扼杀在摇篮里!
15.实战:OOM 后我如何分析解决的
作者:jasonGeng88
关于内存泄露问题在第一次排查时,往往是有点不知所措的。我们需要有正确的方法和手段,配上好用的工具,这样在解决问题时,才能游刃有余。当然对JAVA内存的基础知识也是必不可少的,这时你定位问题的关键,不然就算工具告诉你这块有错,你也不能定位原因。
16.导致程序出现OOM的因素,夜深人静的时候,程序OOM异常追踪
作者:兔兔七
作为Java程序员, 除了享受垃圾回收机制带来的便利外, 还深受OOM(Out Of Memory)的困惑和折磨。
作者故障定位中查到的N多OOM问题,原因大多为以下3类
1、线程池不管理式滥用
2、本地缓存滥用(如只需要使用Node的id,但将整个Node所有信息都缓存)
3、特殊场景考虑不足(如采用单线程处理复杂业务,环境震荡+多设备下积压任务暴增)
17.fastJson与一起堆内存溢出'血案'
作者:landon30
场景:QA同学反映登录不上服务器
排查过程:日志级别——JVM命令级别——专业工具级别
分析过程:观察数据——尝试重试——山重水复疑无路——源代码跟踪——问题初步总结
解决问题:为什么cost会那么大?刚才我们已经基本肯定是因为错误模式下的反序列化会
导致cost字段会越来越大,那么也不至于上亿次吧?这个我大概查了一下代码,很大几率
是好友推荐模块和相关模块。相关代码需要较频繁的对于离线镜像反序列化或者存在类似心跳业务处理。解决办法很简单,就是一定要记住fastjson序列化的时候要加上IgnoreNonFieldGetter就可以了。
18.一次 Java 进程 OOM 的排查分析(glibc 篇)
作者:挖坑的张师傅
遇到了一个 glibc 导致的内存回收问题,查找原因和实验的的过程是比较有意思的,主要会涉及到下面这些:
- Linux 中典型的大量 64M 内存区域问题
- glibc 的内存分配器 ptmalloc2 的底层原理
- 如何写一个自定义的 malloc hook 动态链接库 so
- glibc 的内存分配原理(Arena、Chunk 结构、bins 等)
- malloc_trim 对内存真正回收的影响
- gdb 的 heap 调试工具使用
- jemalloc 库的介绍与应用
19.垃圾回收-实战篇
作者:geekoftaste
本文通过详细介绍了 JVM 参数及 GC 日志, OOM 发生的原因及相应的调试工具,相信读者应该掌握了基本的 MAT,jvisualvm 这些工具排查问题的技巧,不过这些工具的介绍本文只是提到了一些皮毛,大家可以在再深入了解相应工具的一些进阶技能,这能对自己排查问题等大有裨益!文中的例子大家可以去试验一下,修改一下参数看下会发生哪些神奇的现象,亲自动手做一遍能对排查问题的思路更加清晰哦。
20.使用XPocket插件JConsole排查线上OOM异常案例
作者:鸠摩
XPocket插件JConsole主要用于内存问题的排查,能够对堆中的Eden、Survivor、Old区以及堆外的Metaspace、Code Cache等区域进行观察。JConsole能够排查Java进程内存的使用情况,特别是在排查过程中要进行多次打印,比对数值来发现问题。如果要进一步在代码级别定位问题,还可以使用XPocket中的其它插件进行辅助定位。
OOM问题种类繁多,在真实的业务场景中,环境也更加复杂,需要具体问题具体分析,这就需要对各种OOM的产生原理足够了解和熟悉。本文收录了包含大佬总结、源码解析以及大量的排查案例在内的20篇文章,旨在帮助读者补充学习相关知识点,从理论和实战中学习方法,汲取经验,避免踩坑。
第三部分:测验篇
看到这里的你,已经超越好多好多人啦~要不要来参加一下测验?看看你的学习效果如何!