美团面试:说说Java OOM的场景和解决方案?
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
说说Java OOM的场景和解决方案?
最近有小伙伴在面试美团,小伙伴之前虽然遇到过,但是没有成系统的总结过,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的总结,大家一定要多看几遍,按照尼恩的套路去说,展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V172版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取
首先,说说什么是OOM?
OOM 全称 “Out Of Memory”,表示内存耗尽。
官方说明: 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 因为没有足够的内存来为对象分配空间,并且垃圾回收器也已经没有空间可供回收时,就会抛出这个错误。(注:非exception,已经严重到不足以被应用处理)。
为什么会出现 OOM,一般由这些问题引起
- 分配过少:JVM 初始化内存小,业务使用了大量内存;或者不同 JVM 区域分配内存不合理
- 内存泄漏:某一个对象被频繁申请,不用了之后却没有被释放,发生内存泄漏,导致内存耗尽
内存泄漏:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了。因为申请者不用了,而又不能被虚拟机分配给别人用
内存溢出:申请的内存超出了 JVM 能提供的内存大小,此时称之为溢出
内存泄漏持续存在,最后一定会溢出,两者是因果关系
Java OOM的三大核心场景
场景一、堆内存OOM
OOM的场景和解决方案
分析方法通常有两种:
-
类型一:在线分析,这个属于轻量级的分析:
-
类型二:离线分析,这个属于轻量级的分析:
类型一:在线OOM分析,这个属于轻量级的分析:
在线分析方法一: 使用 jmap 分析TOP N对象
jmap(Java Memory Map)是jdk自带的java内存映像工具,使用jmap能够系统运行时的内存信息,同时能够将内存dump下来,分析内存泄露的问题。
-
第一步:jmap 查看进程中占用资源最大的前N个对象,
-
第二步:知道哪个对象消耗内存了,再去定位代码就不难了。然后 导出 快照文件 jmap -dump:live,format=b,file=文件路径/文件名 pid
这里我们使用它 -dump 选项,将内存信息dump到服务器某个地方,然后传到本地使用内存分析工具MAT进行内存分析。
jmap -dump:live,format=b,file=文件路径/文件名 pid
live:就是只dump 活着的对象 format=b 使用二进制 file= 快照文件保存路径
在线分析方法二: 使用 Arthas 在线分析OOM
使用 Arthas 火焰图,分析TOP N对象 和调用堆栈
请参见视频, 和尼恩的《Arthas 学习圣经 v2》 最新版本
类型二:离线OOM分析,这个属于轻量级的分析:
第一步:使用Java内存快照工具:jmap 生成堆转储快照(一般称为headdump或dump文件)。
或者从服务器copy OOM自动dump出来的dump文件。
下面来一份JDK8的JVM参数默认配置
-Xms2g -Xmx2g (按不同容器,4G及以下建议为50%,6G以上,建议设置为70%)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=512m
-Xss256k
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:AutoBoxCacheMax=20000
-XX:+HeapDumpOnOutOfMemoryError (当JVM发生OOM时,自动生成DUMP文件)
-XX:HeapDumpPath=/usr/local/logs/gc/
-XX:ErrorFile=/usr/local/logs/gc/hs_err_%p.log (当JVM发生崩溃时,自动生成错误日志)
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/usr/local/heap-dump/
-
-XX:+HeapDumpOnOutOfMemoryError
从字面就可以很容易的理解,在发生OutOfMemoryError异常时,进行堆的Dump,这样就可以获取异常时的内存快照了。
-
-XX:HeapDumpPath=/usr/local/heap-dump/
这个也很好理解,就是配置HeapDump的路径,
方便我们管理,这里我们配置为/usr/local/heap-dump/,当然你也可以根据自己的需要,定义为其他的目录。
JVM相关的启动参数 给出一些实战经验,让工作中更加从容:
-
调优参数务必加上
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=
,发生OOM让JVM自动dump出内存,方便后续分析问题解决问题 -
堆内存不要设置的特别大,因为你设置的特别大,发生OOM时生成的dump文件就特别大,不好分析。建议不超过8G。
-
想主动dump出JVM内存,有挺多方式,但不管哪种方式,主动dump内存会引发STW,请线上压力最小的时间段操作。
即通过arthas提供的命令heapdump主动dump出JVM的内存,这个操作会引发FGC,背后是STW,操作时请选择好时机,不然老板可能提刀来见。
第2步:导入到jvisualvm进行分析
场景二:元空间(MetaSpace) OOM
什么是元空间(MetaSpace)
JDK8 HotSpot JVM 将移除永久区,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace);这与Oracle JRockit 和IBM JVM’s很相似,如下图所示
这意味着不会再有java.lang.OutOfMemoryError: PermGen 问题,也不再需要你进行调优及监控内存空间的使用……
但请等等,这么说还为时过早。
在默认情况下,这些改变是透明的,接下来我们的展示将使你知道仍然要关注类元数据内存的占用。
请一定要牢记,元空间在直接内存,但是没有 消除类和类加载器导致的内存泄漏。
由于永久代PermGen 空间被移除。所以,JVM 8的参数:PermSize 和 MaxPermSize 会被忽略并给出警告(如果在启用时设置了这两个参数)。
元空间是方法区在HotSpot JVM
中的实现,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。理论上取决于32位/64位系统可虚拟的内存大小,可见也不是无限制的,需要配置参数。
元空间(Metaspace) 垃圾回收,会对僵死的类及类加载器的垃圾回收会进行回收,元空间(Metaspace) 垃圾回收的时机是,在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。
元空间(Metaspace) 容量
默认情况下,类元数据只受可用的本地内存限制(容量取决于是32位或是64位操作系统的可用虚拟内存大小)。
一般情况下避免 MetaSpace 耗尽内存,都会设置一个 MaxMetaSpaceSize参数,MaxMetaspaceSize用于限制本地内存分配给类元数据的大小。如果没有指定这个参数,元空间会在运行时根据需要动态调整。
动态的调整会造成元空间数据的复制和GC的消耗,为了避免弹性伸缩带来的额外 GC 消耗,我们会将-XX:MetaSpaceSize和-XX:MaxMetaSpaceSize两个值设置为固定的,但是这样也会导致在空间不够的时候无法扩容,然后频繁地触发 GC,最终 OOM。
在运行过程中,如果实际大小小于这个值,JVM 就会通过 -XX:MinMetaspaceFreeRatio 和 -XX:MaxMetaspaceFreeRatio 两个参数动态控制整个 MetaSpace 的大小。监控和调整元空间对于减小垃圾回收频率和减少延时是很有必要的。
持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄漏或是大小设置不合适。
元空间(Metaspace) OOM现象
JVM 在启动后或者某个时间点开始,MetaSpace 的已使用大小在持续增长,同时每次 GC 也无法释放,调大 MetaSpace 空间也无法彻底解决。
元空间(Metaspace) OOM 原因
核心原因:生成大量动态类
比如spring的BeanUtils的拷贝对象,json的序列化大量使用反射,
而反射在大量使用时,因为使用了缓存的原因,导致ClassLoader和它引用的Class等对象不能被回收,
反射(包括上面提到的spring的BeanUtils的拷贝对象,json的序列化),而反射在大量使用时,因为使用了缓存的原因,导致ClassLoader和它引用的Class等对象不能被回收,
如何定位和解决
分析dump文件,一般会在日志中发现了“Metaspace OOM”的提示
元空间(Metaspace) OOM 解决办法:
- 减少代码中,使用反射的情况,或者对反射进行优化。
- 测试出服务实例的能力上限,进行服务的过载保护比如(限流等),防止突发流量将服务
场景三:堆外内存 OOM
堆外内存 OOM 现象
-
现象1: Java 进程的 RES 甚至超过了 -Xmx 的大小
-
现象2: Java 进程假死
Java 进程的 RES 甚至超过了 -Xmx 的大小 怎么看呢?通过 top 命令发现 Java 进程的 RES 甚至超过了** -Xmx 的大小。出现这些现象时,基本可以确定是出现了堆外内存泄漏。
使用top命令查看内存和cpu占用高的java进程,使用下面的命令:
top -c -p $(pgrep -d',' -f java)
top命令查看进程信息, 主要的字段含义如下:
- PID:进程的标识符。
- USER:运行进程的用户名。
- PR(优先级):进程的优先级。
- NI(Nice值):进程的优先级调整值。
- VIRT(虚拟内存):进程使用的虚拟内存大小。
- RES(常驻内存):进程实际使用的物理内存大小。
- SHR(共享内存):进程共享的内存大小。
- %CPU:进程占用 CPU 的使用率。
- %MEM:进程占用内存的使用率。
- TIME+:进程的累计 CPU 时间。
top命令的res表示实际占用的内存,RES(Resident Set Size)是用来表示进程占用的物理内存的指标之一,它的单位是KB(千字节)。
具体地说,RES是指当前进程正在使用的物理内存大小,它包括了进程自身和它所拥有的子进程使用的内存,但不包括被共享的内存和被交换到磁盘上的内存。
res可能比xmx设置的要大, 因为统计内容不同
- xmx只是堆内存(包括新生代(eden,from,to),老年代),
- res范围更广,还包括metaDate,堆外内存等,
堆外内存 OOM 原因
JVM 的堆外内存泄漏,主要有两种的原因:
- 通过 UnSafe#allocateMemory,ByteBuffer#allocateDirect 主动申请了堆外内存而没有释放,常见于 NIO、Netty 等相关组件。
- 代码中有通过 JNI 调用 Native Code 申请的内存没有释放。
堆外内存解决OOM对策
-
进行线上指标监控
通过反射获取堆外内存的指标,并且通过在线Prometheus+grafana进行采集和 监控,如果堆外内存一直增长,就大概率泄漏
-
内存泄漏检测进行检测,然后根据找到泄漏的内存,进行Netty引用计数的清零
一般泄漏都发生在最后一次使用后忘记调用释放方法造成
通过Netty自带内存泄漏检测工具,配合压力测试,进行内存泄露检测, 解决OOM之后再上线。
说在最后:有问题找老架构取经
以上的内容,如果大家能对答如流,如数家珍,基本上 面试官会被你 震惊到、吸引到。
最终,让面试官爱到 “不能自已、口水直流”。offer, 也就来了。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。很多小伙伴刷完后, 吊打面试官, 大厂横着走。
在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
另外,如果没有面试机会,可以找尼恩来改简历、做帮扶。
遇到职业难题,找老架构取经, 可以省去太多的折腾,省去太多的弯路。
尼恩指导了大量的小伙伴上岸,前段时间,刚指导一个40岁+被裁小伙伴,拿到了一个年薪100W的offer。
狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。
尼恩技术圣经系列PDF
- 《NIO圣经:一次穿透NIO、Selector、Epoll底层原理》
- 《Docker圣经:大白话说Docker底层原理,6W字实现Docker自由》
- 《K8S学习圣经:大白话说K8S底层原理,14W字实现K8S自由》
- 《SpringCloud Alibaba 学习圣经,10万字实现SpringCloud 自由》
- 《大数据HBase学习圣经:一本书实现HBase学习自由》
- 《大数据Flink学习圣经:一本书实现大数据Flink自由》
- 《响应式圣经:10W字,实现Spring响应式编程自由》
- 《Go学习圣经:Go语言实现高并发CRUD业务开发》
……完整版尼恩技术圣经PDF集群,请找尼恩领取
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》PDF,请到下面公号【技术自由圈】取↓↓↓