OOM 表示程式存在漏洞,可能是程式碼或 JVM 參數配置引起的。這篇文章跟讀者聊聊,Java 進程觸發了 OOM 後如何排查。
#常說對生產環境保持敬畏之心,快速解決問題也是一種敬畏的表現

為什麼OOM
OOM 全名為“Out Of Memory”,表示記憶體耗盡。當JVM 因為沒有足夠的記憶體來為物件分配空間,而垃圾回收器也已經沒有空間可回收時,就會拋出這個錯誤
為什麼會出現OOM,一般由這些問題引起
分配過少:JVM 初始化記憶體小,業務使用了大量記憶體;或不同JVM 區域分配記憶體不合理 程式碼漏洞:某一個對象經常被申請,不用了之後卻沒有被釋放,導致記憶體耗盡
記憶體洩漏:申請使用完的記憶體沒有釋放,導致虛擬機器不能再使用該內存,此時這段內存就洩漏了。因為申請者不用了,但又不能被虛擬機器分配給別人用
記憶體溢出:申請的記憶體超出了JVM 所能提供的記憶體大小,此時稱之為溢位
記憶體洩漏持續存在,最後一定會溢出,兩者是因果關係
常見的OOM
##比較常見的OOM 類型有以下幾種java.lang.OutOfMemoryError: PermGen space
Java7 永久代(方法區)溢出,它用於儲存已被虛擬機器加載的類別資訊、常數、靜態變數、即時編譯器編譯後的程式碼等資料。每當一個類別初次載入的時候,元資料都會存放到永久代一般出現於大量 Class 物件或 JSP 頁面,或採用 CgLib 動態代理技術導致我們可以透過-XX:PermSize
和-XX:MaxPermSize
修改方法區大小
##Java8 將永久代變更為元空間,並報錯:java.lang.OutOfMemoryError: Metadata space,元空間記憶體不足預設進行動態擴充
java.lang.StackOverflowError
#虛擬機棧溢出,一般是由於程式中存在死迴圈或深度遞歸呼叫 造成的。如果堆疊大小設定過小也會出現溢出,可以透過-Xss 設定堆疊的大小
java.lang.OutOfMemoryError: Java heap space
Java 堆記憶體溢出,溢出的原因一般由於JVM 堆記憶體設定不合理或者記憶體洩漏導致
如果是記憶體洩漏,可以透過工具查看洩漏物件到GC Roots 的參考鏈。掌握了洩漏對象的類型資訊以及GC Roots 引用鏈信息,就可以精準地定位出洩漏代碼的位置如果不存在內存洩漏,就是內存中的對象確實都還必須存活著,那就應該檢查虛擬機器的堆疊參數(-Xmx 與-Xms),查看是否可以將虛擬機器的記憶體調大些小結:方法區和虛擬機器棧的溢位場景不在本篇過多討論,下面主要講解常見的Java 堆空間的OOM 排查想法查看JVM 記憶體分佈
假設我們Java 應用PID 為15162,輸入指令檢視JVM 記憶體分佈jmap -heap 15162
[xxx@xxx ~]# jmap -heap 15162 Attaching to process ID 15162, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.161-b12 using thread-local object allocation. Mark Sweep Compact GC Heap Configuration: MinHeapFreeRatio = 40 # 最小堆使用比例 MaxHeapFreeRatio = 70 # 最大堆可用比例 MaxHeapSize = 482344960 (460.0MB) # 最大堆空间大小 NewSize = 10485760 (10.0MB) # 新生代分配大小 MaxNewSize = 160759808 (153.3125MB) # 最大新生代可分配大小 OldSize = 20971520 (20.0MB) # 老年代大小 NewRatio = 2 # 新生代比例 SurvivorRatio = 8 # 新生代与 Survivor 比例 MetaspaceSize = 21807104 (20.796875MB) # 元空间大小 CompressedClassSpaceSize = 1073741824 (1024.0MB) # Compressed Class Space 空间大小限制 MaxMetaspaceSize = 17592186044415 MB # 最大元空间大小 G1HeapRegionSize = 0 (0.0MB) # G1 单个 Region 大小 Heap Usage: # 堆使用情况 New Generation (Eden + 1 Survivor Space): # 新生代 capacity = 9502720 (9.0625MB) # 新生代总容量 used = 4995320 (4.763908386230469MB) # 新生代已使用 free = 4507400 (4.298591613769531MB) # 新生代剩余容量 52.56726495150862% used # 新生代使用占比 Eden Space: capacity = 8454144 (8.0625MB) # Eden 区总容量 used = 4029752 (3.8430709838867188MB) # Eden 区已使用 free = 4424392 (4.219429016113281MB) # Eden 区剩余容量 47.665996699370154% used # Eden 区使用占比 From Space: # 其中一个 Survivor 区的内存分布 capacity = 1048576 (1.0MB) used = 965568 (0.92083740234375MB) free = 83008 (0.07916259765625MB) 92.083740234375% used To Space: # 另一个 Survivor 区的内存分布 capacity = 1048576 (1.0MB) used = 0 (0.0MB) free = 1048576 (1.0MB) 0.0% used tenured generation: # 老年代 capacity = 20971520 (20.0MB) used = 10611384 (10.119804382324219MB) free = 10360136 (9.880195617675781MB) 50.599021911621094% used 10730 interned Strings occupying 906232 bytes.#透過檢視JVM 記憶體分配以及執行時間使用情況,可以判斷記憶體分配是否合理
另外,可以在JVM 運行時查看最耗費資源的對象,jmap -histo:live 15162 | more
JVM 記憶體物件清單依照物件所佔記憶體大小排序
instances:實例數 bytes:單位byte -
class name:類別名稱

明顯看到CustomObjTest
物件實例以及佔用記憶體過多
可惜的是,方案有其限制,因為它只能排查物件佔用記憶體過高問題
其中"[" 代表數組,例如"[C" 代表Char 數組,"[B" 代表Byte 數組。如果陣列記憶體佔用過多,我們不知道哪些物件持有它,所以就需要Dump 記憶體進行離線分析
jmap -histo:live
執行此命令,JVM 會先觸發GC,再統計資訊
Dump 檔案分析
Dump 檔案是Java 進程的記憶體鏡像,其中主要包含系統資訊、虛擬機器屬性、完整的線程Dump、所有類別和物件的狀態 等資訊
當程式發生記憶體溢位或GC 例外狀況時,懷疑JVM 發生了記憶體洩漏,這時我們就可以匯出Dump 檔案分析
JVM 啟動參數配置新增以下參數
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./(参数为 Dump 文件生成路径)
当 JVM 发生 OOM 异常自动导出 Dump 文件,文件名称默认格式:
java_pid{pid}.hprof
上面配置是在应用抛出 OOM 后自动导出 Dump,或者可以在 JVM 运行时导出 Dump 文件
jmap -dump:file=[文件路径] [pid] # 示例 jmap -dump:file=./jvmdump.hprof 15162
在本地写一个测试代码,验证下 OOM 以及分析 Dump 文件
设置 VM 参数:-Xms3m -Xmx3m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ public static void main(String[] args) { List<Object> oomList = Lists.newArrayList(); // 无限循环创建对象 while (true) { oomList.add(new Object()); } }
通过报错信息得知,java heap space
表示 OOM 发生在堆区,并生成了 hprof 二进制文件在当前文件夹下

JvisualVM 分析
Dump 分析工具有很多,相对而言 JvisualVM、JProfiler、Eclipse Mat,使用人群更多一些。下面以 JvisualVM 举例分析 Dump 文件

列举两个常用的功能,第一个是能看到触发 OOM 的线程堆栈,清晰得知程序溢出的原因

第二个就是可以查看 JVM 内存里保留大小最大的对象,可以自由选择排查个数

点击对象还可以跳转具体的对象引用详情页面

文中 Dump 文件较为简单,而正式环境出错的原因五花八门,所以不对该 Dump 文件做深度解析
注意:JvisualVM 如果分析大 Dump 文件,可能会因为内存不足打不开,需要调整默认的内存
總結回顧
線上如遇到JVM 記憶體溢出,可以分以下幾步排查
jmap -heap
查看是否記憶體分配過小#jmap -histo
查看是否有明顯的物件分配過多且沒有釋放情況jmap -dump
匯出JVM 目前記憶體快照,使用JDK 自帶或MAT 等工具分析快照
如果上面還不能定位問題,那麼需要排查應用程式是否不斷創建資源,例如網路連線或線程,都可能會導致系統資源耗盡。
以上是某團面試:如果線上遇到了OOM,該如何檢查?如何解決?哪些方案?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。