Java中內(nèi)存問題之OOM詳解
一. StackOverflowError
1.1 bug
public class StackOverflowErrorDemo {
public static void main(String[] args) {
javaKeeper();
}
private static void javaKeeper() {
javaKeeper();
}
}JVM 虛擬機(jī)棧是有深度的,在執(zhí)行方法的時(shí)候會伴隨著入棧和出棧,上邊的方法可以看到,main 方法執(zhí)行后不停的遞歸,遲早把棧撐爆了
Exception in thread "main" java.lang.StackOverflowError at oom.StackOverflowErrorDemo.javaKeeper(StackOverflowErrorDemo.java:15)
1.2 原因分析
- 無限遞歸循環(huán)調(diào)用(最常見原因),要時(shí)刻注意代碼中是否有了循環(huán)調(diào)用方法而無法退出的情況
- 執(zhí)行了大量方法,導(dǎo)致線程??臻g耗盡
- 方法內(nèi)聲明了海量的局部變量
- native 代碼有棧上分配的邏輯,并且要求的內(nèi)存還不小,比如 java.net.SocketInputStream.read0 會在棧上要求分配一個(gè) 64KB 的緩存(64位 Linux)
1.3 解決方案
- 修復(fù)引發(fā)無限遞歸調(diào)用的異常代碼, 通過程序拋出的異常堆棧,找出不斷重復(fù)的代碼行,按圖索驥,修復(fù)無限遞歸 Bug
- 排查是否存在類之間的循環(huán)依賴(當(dāng)兩個(gè)對象相互引用,在調(diào)用toString方法時(shí)也會產(chǎn)生這個(gè)異常)
- 通過 JVM 啟動參數(shù) -Xss 增加線程棧內(nèi)存空間, 某些正常使用場景需要執(zhí)行大量方法或包含大量局部變量,這時(shí)可以適當(dāng)?shù)靥岣呔€程??臻g限制
二. Java heap space
Java 堆用于存儲對象實(shí)例,我們只要不斷的創(chuàng)建對象,并且保證 GC Roots 到對象之間有可達(dá)路徑來避免 GC 清除這些對象,那隨著對象數(shù)量的增加,總?cè)萘坑|及堆的最大容量限制后就會產(chǎn)生內(nèi)存溢出異常。
Java 堆內(nèi)存的 OOM 異常是實(shí)際應(yīng)用中最常見的內(nèi)存溢出異常。
2.1 bug
/** * JVM參數(shù):-Xmx12m */public class JavaHeapSpaceDemo {<!-- --> static final int SIZE = 2 * 1024 * 1024; public static void main(String[] a) {<!-- --> int[] i = new int[SIZE]; }}
代碼試圖分配容量為 2M 的 int 數(shù)組,如果指定啟動參數(shù) -Xmx12m ,分配內(nèi)存就不夠用,就類似于將 XXXL 號的對象,往 S 號的 Java heap space 里面塞。
/**
* JVM參數(shù):-Xmx12m
*/
public class JavaHeapSpaceDemo {
static final int SIZE = 2 * 1024 * 1024;
public static void main(String[] a) {
int[] i = new int[SIZE];
}
}2.2 原因分析
- 請求創(chuàng)建一個(gè)超大對象,通常是一個(gè)大數(shù)組
- 超出預(yù)期的訪問量/數(shù)據(jù)量,通常是上游系統(tǒng)請求流量飆升,常見于各類促銷/秒殺活動,可以結(jié)合業(yè)務(wù)流量指標(biāo)排查是否有尖狀峰值
- 過度使用終結(jié)器(Finalizer),該對象沒有立即被 GC
- 內(nèi)存泄漏(Memory Leak),大量對象引用沒有釋放,JVM 無法對其自動回收,常見于使用了 File 等資源沒有回收
2.3 解決方案
針對大部分情況,通常只需要通過 -Xmx 參數(shù)調(diào)高 JVM 堆內(nèi)存空間即可。如果仍然沒有解決,可以參考以下情況做進(jìn)一步處理:
- 如果是超大對象,可以檢查其合理性,比如是否一次性查詢了數(shù)據(jù)庫全部結(jié)果,而沒有做結(jié)果數(shù)限制
- 如果是業(yè)務(wù)峰值壓力,可以考慮添加機(jī)器資源,或者做限流降級。
- 如果是內(nèi)存泄漏,需要找到持有的對象,修改代碼設(shè)計(jì),比如關(guān)閉沒有釋放的連接
內(nèi)存泄露和內(nèi)存溢出
內(nèi)存溢出(out of memory),是指程序在申請內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,出現(xiàn)out of memory;
比如申請了一個(gè) Integer,但給它存了 Long 才能存下的數(shù),那就是內(nèi)存溢出。
內(nèi)存泄露( memory leak),是指程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無論多少內(nèi)存,遲早會被占光。
memory leak 最終會導(dǎo)致 out of memory!
到此這篇關(guān)于Java中內(nèi)存管理的OOM詳解的文章就介紹到這了,更多相關(guān)Java的OOM內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
為什么wait和notify必須放在synchronized中使用
這篇文章主要介紹了為什么wait和notify必須放在synchronized中使用,文章圍繞主題的相關(guān)問題展開詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考以參考一下2022-05-05
Springboot項(xiàng)目優(yōu)雅地處理日志的方法詳解
這篇文章主要介紹了Springboot項(xiàng)目---優(yōu)雅地處理日志,本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07
JDK生成WebService客戶端代碼以及調(diào)用方式
WebService 是一種跨編程語言和跨操作系統(tǒng)平臺的遠(yuǎn)程調(diào)用技術(shù),下面這篇文章主要給大家介紹了關(guān)于JDK生成WebService客戶端代碼以及調(diào)用方式的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08
spring-boot整合Micrometer+Prometheus的詳細(xì)過程
這篇文章主要介紹了springboot整合Micrometer+Prometheus的詳細(xì)過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-05-05
詳解一個(gè)簡單的Servlet容器的設(shè)計(jì)與實(shí)現(xiàn)

