美团面试:说说Java OOM的三大场景和解决方案?

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领

免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


美团面试:说说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,一般由这些问题引起

  1. 分配过少:JVM 初始化内存小,业务使用了大量内存;或者不同 JVM 区域分配内存不合理
  2. 内存泄漏:某一个对象被频繁申请,不用了之后却没有被释放,发生内存泄漏,导致内存耗尽

内存泄漏:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了。因为申请者不用了,而又不能被虚拟机分配给别人用

内存溢出:申请的内存超出了 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相关的启动参数 给出一些实战经验,让工作中更加从容:

  1. 调优参数务必加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=,发生OOM让JVM自动dump出内存,方便后续分析问题解决问题

  2. 堆内存不要设置的特别大,因为你设置的特别大,发生OOM时生成的dump文件就特别大,不好分析。建议不超过8G。

  3. 想主动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个专题

免费获取11个技术圣经PDF:

posted @ 2024-02-23 21:58  疯狂创客圈  阅读(404)  评论(0编辑  收藏  举报