哈嘍睬魂,大家好,本期這一系列镀赌,咱們聊一下發(fā)生OOM咱們應(yīng)該怎么處理氯哮。
1. Java堆溢出
Java堆用于存儲(chǔ)對(duì)象實(shí)例,只要不斷創(chuàng)建對(duì)象商佛,并且保證GC Roots
到對(duì)象
之間有可達(dá)路徑
避免垃圾回收機(jī)制消除這些對(duì)象喉钢,那么對(duì)象數(shù)量到達(dá)最大堆容量限制后就會(huì)產(chǎn)生內(nèi)存溢出異常。
要解決這個(gè)問(wèn)題良姆,一般的手段是先通過(guò)內(nèi)存影像分析工具JVM那點(diǎn)事-內(nèi)存溢出如何處理(1)——MAT工具的下載使用對(duì)Dump出來(lái)的堆轉(zhuǎn)儲(chǔ)快照進(jìn)行分析肠虽。重點(diǎn)是確定內(nèi)存對(duì)象是否是有必要的。也就是要先分清楚到底是出現(xiàn)了內(nèi)存泄露(Memory Leak
)還是內(nèi)存溢出(Memory Overflow
)玛追。
如果是內(nèi)存泄露舔痕,可以進(jìn)一步查看泄露對(duì)象到GCRoots
的引用鏈,于是就能找到泄露對(duì)象通過(guò)怎么樣的路徑(也就是dominator ['d?m?ne?t?]
支配者)與GC相關(guān)聯(lián)并導(dǎo)致GC無(wú)法自動(dòng)回收它們。
如果不存在泄露伯复,換句話(huà)說(shuō)慨代。就是內(nèi)存中的對(duì)象確實(shí)都還必須存活著,那么就需要檢查JVM的堆參數(shù)(-XMx
和-XMs
)與物理內(nèi)存對(duì)比是否還可以調(diào)大啸如。從代碼檢查角度侍匙,判斷是否存在某些對(duì)象聲明周期過(guò)長(zhǎng),持有狀態(tài)時(shí)間過(guò)長(zhǎng)叮雳,應(yīng)嘗試減少程序運(yùn)行期的內(nèi)存消耗想暗。
2. 虛擬機(jī)棧和本地方法棧溢出
棧容量只由-Xss
參數(shù)設(shè)置。關(guān)于虛擬機(jī)棧和本地方法棧帘不,java虛擬機(jī)描述了兩種異常:
- 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的最大深度说莫,將拋出
StackOverflowError
([fl??]
流動(dòng))。 - 如果虛擬機(jī)在擴(kuò)展棧時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存空間寞焙,則拋出
OutOfMemoryError
储狭。
操作系統(tǒng)分配給每個(gè)進(jìn)程的內(nèi)存是有限的(JVM是一個(gè)進(jìn)程)虛擬機(jī)提供了參數(shù)來(lái)控制Java
堆(-Xmx
)和方法區(qū)(MaxPermSize
[p?:m]電卷發(fā)
)內(nèi)存的最大值,程序計(jì)數(shù)器銷(xiāo)毀內(nèi)存很小捣郊,可以忽略掉辽狈。剩下的內(nèi)存就由虛擬機(jī)棧
和本地方法棧
瓜分。
若是建立多線程導(dǎo)致的內(nèi)存溢出呛牲,在不減少線程數(shù)的情況下刮萌,可以通過(guò)減少最大堆和減少棧容量來(lái)?yè)Q取更多的線程。
3. 方法區(qū)和運(yùn)行時(shí)常量池溢出
我們可以通過(guò)-XX:PermSize
和-XX:MaxPermSize
限制方法區(qū)的大小娘扩。方法區(qū)存放Class
相關(guān)信息着茸,如類(lèi)名、訪問(wèn)修飾符琐旁、常量池涮阔、字段描述、方法描述等⌒牛現(xiàn)在很多主流框架澎语,如Spring
、Hibernate
验懊,在對(duì)類(lèi)進(jìn)行增強(qiáng)時(shí)擅羞,都會(huì)使用CGLib
這類(lèi)字節(jié)碼技術(shù),增強(qiáng)的類(lèi)越多义图,需要越大的方法區(qū)保證動(dòng)態(tài)生成的Class
可以加載入內(nèi)存减俏。可能會(huì)導(dǎo)致方法區(qū)內(nèi)存溢出碱工。
4. 本機(jī)直接內(nèi)存溢出
JVM那點(diǎn)事-JVM內(nèi)存結(jié)構(gòu)可以了解一下堆外內(nèi)存娃承。這里簡(jiǎn)單介紹下:(直接內(nèi)存大多情況下被稱(chēng)為堆外內(nèi)存奏夫,自從java引入NIO
之后,堆外內(nèi)存使用越來(lái)越普遍历筝,通過(guò)native
方法可以分配堆外內(nèi)存酗昼,通過(guò)DirectByteBuffer
對(duì)象來(lái)操縱)。
DirectMemory
容量可以通過(guò) -XX:MaxDirectMemorySize
參數(shù)來(lái)設(shè)置最大可用直接內(nèi)存梳猪,如果啟動(dòng)時(shí)未設(shè)置則默認(rèn)為最大堆內(nèi)存大小麻削,即與 -Xmx
相同。即假如最大堆內(nèi)存為1G春弥,則默認(rèn)直接內(nèi)存也為1G呛哟,那么 JVM 最大需要的內(nèi)存大小為2G多一些。當(dāng)直接內(nèi)存達(dá)到最大限制時(shí)就會(huì)觸發(fā)GC匿沛,如果回收失敗則會(huì)引起OutOfMemoryError
扫责。
若是發(fā)生直接內(nèi)存溢出的情況,解決方案可以擴(kuò)大堆外內(nèi)存或者禁止netty使用堆外內(nèi)存逃呼,轉(zhuǎn)用堆內(nèi)內(nèi)存鳖孤。
擴(kuò)大堆外內(nèi)存:
-XX:MaxDirectMemorySize=1024m
禁用堆外內(nèi)存:
-Dio.netty.noPreferDirect=true \
-Dio.netty.leakDetectionLevel=advanced \
這里的操作要看服務(wù)器的內(nèi)存大小,內(nèi)存足夠大蜘渣,直接擴(kuò)大堆外內(nèi)存即可淌铐。