美團面試:說說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:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章