Java內存和OOM情況處理

JVM在執行Java程序時過程中會把內存劃分爲幾個數據區域,報考方法區、虛擬機棧、本地方法棧、堆、程序計數器等。如下圖所示
圖片

  • 程序計數器:當前線程所執行的字節碼的行號指示器,每條線程都需要一個獨立的程序計數器;執行的是java方法,該記錄是正在執行的虛擬機字節碼指令地址,執行的是native方法,計數器值爲空(Undefined),無OOM情況。
  • 虛擬機棧:方法執行並創建棧幀,存儲局部變量表,操作數棧、動態鏈接、方法出口等信息;具有64位長度佔有2個局部變量空間,其餘的佔有1個,並且其空間在編譯的期間完成分配。在線程請求棧深度大於虛擬機允許的深度會拋出StackOverflowError,在擴展無法申請足夠內存會跑出OutOfMemoryError。
  • 本地方法棧:跟虛擬機棧類型,但執行的Native方法,也會拋出StackOverflowError和OutOfMemoryError。
  • :所有對象實例以及數組都要在堆上分配,堆內存不夠或無法擴展會拋出OutOfMemoryError。
  • 方法區:存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,內存不夠分配時也會拋出OutOfMemoryError。

SO和OOM常見異常解決方法

  • Java堆溢出:展示“Java heap space”,需要分清楚是溢出還是泄露,溢出的話重點在確認內存中的對象是否是必須的,如果是必要的,就需要調整虛擬機參數(-Xmx,-Xms);而泄露需要查看對象的GC Roots的引用鏈,看對象的具體引用信息,分析泄露情況
  • 虛擬機棧和本地方法棧溢出:會拋出“java.lang.StackOverflowError”或者“java.lang.OutOfMemoryError”異常,出現這種情況一般都是操作系統分配給進程的內存都是有限制的,可分配內存=操作系統剩餘內存-最大堆容量-最大方法去容量,這樣就會導致可分配內存過小,而每個線程的棧容量越大,可創建的線程數量越小;在不減少線程數的情況下,只能減少最大堆容量和棧容量來獲取更多線程
  • 方法區和運行時常量池溢出:在JDK1.7以後,不會報錯相關錯誤信息,直到系統內存被佔滿,但一個類的要被回收是比較困難的,這樣也會導致方法區的內存溢出,在動態生成類中需要注意類的回收。
  • 本機直接內存溢出:會拋出“java.lang.OutOfMemoryError”異常,在DirectMemory申請內存,計算得知內存無法分配會手動拋出異常,真正申請內存unsafe.allocateMemory(),在OOM之後發現Dump文件很小,而程序中又直接或間接使用了NIO,可以考慮該方面原因

常見導致OOM

  • 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
  • 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
  • 未關閉InputStream/OutputStream;
  • Bitmap使用後未調用recycle()
  • static關鍵字
  • 代碼中存在死循環或循環產生過多重複的對象實體;
  • 使用的第三方軟件中的BUG;
  • 啓動參數內存值設定的過小;
  • 數據庫的cursor沒有關閉;
  • 加載太多資源到內存,導致GC耗時較多
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章