java.lang.OutOfMemoryError:Javaheap space
當堆內存(Heap Space)沒有足夠空間存放新創建的對象時,就會拋出 java.lang.OutOfMemoryError:Javaheap space
錯誤(根據實際生產經驗,可以對程序日誌中的 OutOfMemoryError 配置關鍵字告警,一經發現,立即處理)
原因分析:
Javaheap space
錯誤產生的常見原因可以分爲以下幾類:
1、請求創建一個超大對象,通常是一個大數組。
2、超出預期的訪問量/數據量,通常是上游系統請求流量飆升,常見於各類促銷/秒殺活動,可以結合業務流量指標排查是否有尖狀峯值。
3、過度使用終結器(Finalizer),該對象沒有立即被 GC。
4、內存泄漏(Memory Leak),大量對象引用沒有釋放,JVM 無法對其自動回收,常見於使用了 File 等資源沒有回收。
解決:
針對大部分情況,通常只需要通過 -Xmx
參數調高 JVM 堆內存空間即可。如果仍然沒有解決,可以參考以下情況做進一步處理:
1、如果是超大對象,可以檢查其合理性,比如是否一次性查詢了數據庫全部結果,而沒有做結果數限制。
2、如果是業務峯值壓力,可以考慮添加機器資源,或者做限流降級。
3、如果是內存泄漏,需要找到持有的對象,修改代碼設計,比如關閉沒有釋放的連接。
java.lang.OutOfMemoryError:GC overhead limit exceeded
當 Java 進程花費 98% 以上的時間執行 GC,但只恢復了不到 2% 的內存,且該動作連續重複了 5 次,就會拋出 java.lang.OutOfMemoryError:GC overhead limit exceeded
錯誤。簡單地說,就是應用程序已經基本耗盡了所有可用內存, GC 也無法回收。
此類問題的原因與解決方案跟 Javaheap space
非常類似,可以參考上文
java.lang.OutOfMemoryError:PermGen space
該錯誤表示永久代(Permanent Generation)已用滿,通常是因爲加載的 class 數目太多或體積太大。
原因分析
永久代存儲對象主要包括以下幾類:
1、加載/緩存到內存中的 class 定義,包括類的名稱,字段,方法和字節碼;
2、常量池;
3、對象數組/類型數組所關聯的 class;
4、JIT 編譯器優化後的 class 信息。
PermGen 的使用量與加載到內存的 class 的數量/大小正相關。
解決方案
根據 Permgen space 報錯的時機,可以採用不同的解決方案,如下所示:
1、程序啓動報錯,修改 -XX:MaxPermSize
啓動參數,調大永久代空間。
2、應用重新部署時報錯,很可能是沒有應用沒有重啓,導致加載了多份 class 信息,只需重啓 JVM 即可解決。
3、運行時報錯,應用程序可能會動態創建大量 class,而這些 class 的生命週期很短暫,但是 JVM 默認不會卸載 class,可以設置 -XX:+CMSClassUnloadingEnabled
和 -XX:+UseConcMarkSweepGC
這兩個參數允許 JVM 卸載 class。
如果上述方法無法解決,可以通過 jmap 命令 dump 內存對象 jmap-dump:format=b,file=dump.hprof<process-id>
,然後利用 Eclipse MAT https://www.eclipse.org/mat 功能逐一分析開銷最大的 classloader 和重複 class。
java.lang.OutOfMemoryError:Metaspace
JDK 1.8 使用 Metaspace 替換了永久代(Permanent Generation),該錯誤表示 Metaspace 已被用滿,通常是因爲加載的 class 數目太多或體積太大。
此類問題的原因與解決方法跟 Permgenspace
非常類似,可以參考上文。需要特別注意的是調整 Metaspace 空間大小的啓動參數爲 -XX:MaxMetaspaceSize
。
java.lang.OutOfMemoryError:Unable to create new native thread
每個 Java 線程都需要佔用一定的內存空間,當 JVM 向底層操作系統請求創建一個新的 native 線程時,如果沒有足夠的資源分配就會報此類錯誤。
原因分析
JVM 向 OS 請求創建 native 線程失敗,就會拋出 Unableto createnewnativethread
,常見的原因包括以下幾類:
1、線程數超過操作系統最大線程數 ulimit 限制;
2、線程數超過 kernel.pid_max(只能重啓);
3、native 內存不足;
該問題發生的常見過程主要包括以下幾步:
1、JVM 內部的應用程序請求創建一個新的 Java 線程;
2、JVM native 方法代理了該次請求,並向操作系統請求創建一個 native 線程;
3、操作系統嘗試創建一個新的 native 線程,併爲其分配內存;
4、如果操作系統的虛擬內存已耗盡,或是受到 32 位進程的地址空間限制,操作系統就會拒絕本次 native 內存分配;
5、JVM 將拋出 java.lang.OutOfMemoryError:Unableto createnewnativethread
錯誤。
解決方案
1、升級配置,爲機器提供更多的內存;
2、降低 Java Heap Space 大小;
3、修復應用程序的線程泄漏問題;
4、限制線程池大小;
5、使用 -Xss 參數減少線程棧的大小;
6、調高 OS 層面的線程最大數:執行 ulimia-a
查看最大線程數限制,使用 ulimit-u xxx
調整最大線程數限制。
ulimit -a .... 省略部分內容 ..... max user processes (-u) 16384