JVM系列之OOM異常

前言

之前我們簡單的介紹了下虛擬機內存結構,今天我們講下虛擬機的內存相關錯誤異常,主要從幾大內存區域分類介紹:Java堆溢出、虛擬機棧和本地方法棧溢出、方法區和運行時常量池溢出、本機直接內存溢出。

 

Java堆溢出

  • 什麼時候產生堆溢出?

    • 堆用於存儲對象實例,只要不斷的創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼在對象數量達到了最大堆容量限制時就會產生堆溢出異常;

  • 堆溢出如何分析?

    • 通過設置參數:-XX:+HeapDumpOnOutOfMemoryError,當虛擬機產生堆溢出異常時就會生成內存堆轉儲快照;

    • 通過分析堆轉儲快照分析堆溢出的原因是存在內存泄露還是內存溢出;

      • 若是內存泄露,可通過進一步查看泄露對象到GC Roots的引用鏈;

      • 若不是內存泄露,即內存溢出,那麼就應當檢查虛擬機的堆參數(-Xmx,-Xms)與物理內存對比是否還能繼續調大,檢查代碼中是否存在某些對象生命週期過長、持有狀態時間過長,嘗試減小程序運行時內存消耗;

 

虛擬機棧和本地方法棧溢出

上節我們有提到某些虛擬機將虛擬機棧和本地方法棧合二爲一,所以棧的容量本質上只由-Xss參數設置

  • 什麼時候產生棧溢出?

    • 當線程請求的棧深度大於虛擬機所允許的最大深度時,拋出StackOverflowError異常;

    • 當虛擬機在擴展棧時無法申請到足夠的內存空間,就會拋出OutOfMemoryError;

    • 單線程環境下,無論是棧幀太大還是虛擬機棧容量太小,當內存無法分配時,都會產生StackOverflowError異常;

    • 多線程環境下,通過不斷創建線程會存在內存溢出情況,這種情況下,爲每個線程的棧分配的內存越大,反而越容易產生內存溢出;

  • 如何分析棧溢出?

    • 操作系統分配給每個線程的內存是有限的,虛擬機提供了堆和方法區兩個部分的最大內存設置,除掉這兩部分內存,剩下的內存基本上就是棧的,每個線程分配到的棧內存越大,可以分配的線程總數就越小,建立線程時就越容易把剩下的內存耗盡,因此這部分溢出解決並沒有太好的方案,只能通過加大物理機的內存;

    • 當多線程環境下時,如果棧溢出是由於建立了太多的線程導致,那麼可以通過減少最大堆和減少棧容量來換取更多的線程數量;

 

方法區和運行時常量池溢出

之前我們提到過運行時常量池是方法區的一部分,所以這兩塊的溢出直接放一起,在JDK1.6及之前,可以通過設置-XX:PermSize和-XX:MaxPermSize來設置方法區大小的初始值和最大值;

  • 什麼時候產生方法區溢出?

    • 我們知道方法區是存放Class的相關信息,因此當運行時產生大量的類,填滿方法區時就會產生溢出異常;

    • 常見的CGLib字節碼增強和動態語言,大量的JSP或動態產生JSP文件,基於OSGI的應用等容易產生方法區溢出;

  • 如何分析方法區溢出?

    • 方法區溢出是很常見的內存溢出異常,在經常產生大量Class的應用中,我們要特別注意類的垃圾回收狀況,即使減少大量類無法回收的問題;

 

本機直接內存溢出

DirectMemory容量可以通過-XX:MaxDirectMemorySize指定,如果不指定,則默認和Java堆最大值一樣(-Xmx);

由本機直接內存溢出一個明顯的特徵是在內存堆轉儲快照文件中不會看見明顯的異常,當發生內存溢出異常之後堆轉儲文件又很小的情況下,就可以檢查程序中是否直接或者間接使用了NIO。

相關文章:JVM系列之Java內存區域 https://blog.csdn.net/chengyabingfeiqi/article/details/106559475

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