前言:
作为一名Java程序员,平时在公司拼命加班、任劳任怨干活,好不容易熬夜开发完系统,测试完毕,Bug修复完毕,然后上线部署系统,系统开始正常运行,终于歇口气能好好放松下,喝杯Java压压惊了,But!上线后最害怕的事是什么?
不用多说,相信大家应该猜到了,一定是事故!很多大公司叫 Case,一旦当系统出现 Case,比如线上核心系统突然宕机不可用,导致几个小时内用户无法下单,进而导致公司损失几百万,甚至几千万;或者公司的某个单点登录系统突然不可用,导致所有用户无法登录APP,也无法下单;或者公司的缓存集群突然全面故障,进而导致公司的全部系统一起瘫痪!或者因为某个明星的突然出轨,结果导致流量集中访问某台服务器,直接把数据库搞挂!
凡是以上种种,都可以称作为:重大Case!一旦有Case,就会有程序员被拉出去祭天,当然这也是网上常见的一个段子(开个玩笑)。
针对这些事故,肯定是需要有人去承担的,重要的是分析到底什么原因导致的事故,谁的责任,后续如何优化以及避免!
那么作为Java程序员而言,先不考虑自己系统外部依赖的缓存、消息队列、数据库等等东西挂掉,就我们自己系统本身而言,最常见的挂掉原因是什么呢?
答案:没错!就是系统OOM(Out Of Memory),也就是所谓的内存溢出!
为什么会出现OOM?
通过之前JVM的一系列学习,相信大家对于JVM内存结构,内存分配,运行原理已经了解的非常清楚了,那么为什么系统会发生OOM呢?
首先给出官方的一段解释:
Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.
意思就是说,当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
为什么会没有内存了呢?原因不外乎有两点:
1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。
在之前没有垃圾自动回收的日子里,比如C语言和C++语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如C++中的new了但是没有delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。
而在Java语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java中的内存泄露,不同于C++中的忘了delete,往往是逻辑上的原因泄露。
其实简单总结一下Java系统为什么发生OOM:不外乎就是不停的往JVM内存中扔东西,导致最终没有足够的内存可以容纳,如下图所示:
一旦系统不停的往内存中大量塞东西,导致JVM内存撑爆,直接OOM,系统瘫痪,那么对于我们来说是致命的打击,直接导致我们的系统停止运转,甚至JVM进程直接崩溃,进程都没了。用户点击APP,网页都没反应。对于程序员的我们来讲可能辛苦开发了几个月的绩效奖金不但拿不到,反而被扣绩效,甚至年终奖也没了。。。弄不好今年还得提前出去找工作了
如何处理OOM呢?
其实最可怕的并不是线上系统发生OOM,最可怕的是很多工程师压根儿就没有这种处理线上系统故障的经验!
当发生OOM之后,根本不知道系统为什么会突然OOM?到底产生了多少对象,为什么会产生这些对象,到底是哪些对象,内存到底分配了多大,如何去排查这个问题,如何解决,一切皆是迷!
我不听,我不听,这肯定不关我的事儿!一定不是我的代码导致的。这是不是你的内心第一想法?
接下来OpenCoder将为大家带来针对各种可能发生OOM的情况,结合内存溢出的一些场景,通过模拟代码真实感受以及如何解决OOM带来的问题,给到大家如何去监控、定位、排查、分析和解决JVM OOM的问题,再也无惧线上系统突然出现的OOM状况!