文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
美团面试:说说Java OOM的场景和解决方案?
尼恩说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
说说Java OOM的场景和解决方案?
最近有小伙伴在面试美团,小伙伴之前虽然遇到过,但是没有成系统的总结过,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的总结,大家一定要多看几遍,按照尼恩的套路去说,展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V172版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】获取
本文目录
目录美团面试:说说Java OOM的场景和解决方案?尼恩说在前面本文目录首先,说说什么是OOM?Java OOM的三大核心场景场景一、堆内存OOM类型一:在线OOM分析,这个属于轻量级的分析:类型二:离线OOM分析,这个属于轻量级的分析:场景二:元空间(MetaSpace) OOM什么是元空间(MetaSpace)元空间(Metaspace) OOM现象元空间(Metaspace) OOM 原因如何定位和解决元空间(Metaspace) OOM 解决办法:场景三:堆外内存 OOM堆外内存 OOM 现象堆外内存 OOM 原因堆外内存解决OOM对策说在最后:有问题找老架构取经技术自由的实现路径:实现你的 架构自由:实现你的 响应式 自由:实现你的 spring cloud 自由:实现你的 linux 自由:实现你的 网络 自由:实现你的 分布式锁 自由:实现你的 王者组件 自由:实现你的 面试题 自由:免费获取11个技术圣经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自由” 。
技术自由的实现路径:
实现你的 架构自由:
《吃透8图1模板,人人可以做架构》
《10Wqps评论中台,如何架构?B站是这么做的!!!》
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
《100亿级订单怎么调度,来一个大厂的极品方案》
《2个大厂 100亿级 超大流量 红包 架构方案》
… 更多架构文章,正在添加中
实现你的 响应式 自由:
《响应式圣经:10W字,实现Spring响应式编程自由》
这是老版本 《Flux、Mono、Reactor 实战(史上最全)》
实现你的 spring cloud 自由:
《Spring cloud Alibaba 学习圣经》 PDF
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
实现你的 linux 自由:
《Linux命令大全:2W多字,一次实现Linux自由》
实现你的 网络 自由:
《TCP协议详解 (史上最全)》
《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》
实现你的 分布式锁 自由:
《Redis分布式锁(图解 – 秒懂 – 史上最全)》
《Zookeeper 分布式锁 – 图解 – 秒懂》
实现你的 王者组件 自由:
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《缓存之王:Caffeine 的使用(史上最全)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》
实现你的 面试题 自由:
4800页《尼恩Java面试宝典 》 40个专题