OOM簡記

OOM簡記

notice: 下面說的比如10M老年代空間埃疫,在10M分配完畢的時(shí)候進(jìn)行FullGC都是簡化的說法栓霜,其實(shí)應(yīng)該是有個(gè)空間分配擔(dān)保機(jī)制的存在横蜒,不會(huì)出現(xiàn)在10M全部使用的情況下才進(jìn)行FullGC的情況。

1. OOME出現(xiàn)的區(qū)域

  1. heap java堆

  2. vm stack 虛擬機(jī)棧

  3. native method stack 本地方法棧

  4. method area 方法區(qū)

  5. direct memory 直接內(nèi)存

ps. 除了program counter register 程序計(jì)數(shù)器外的其他內(nèi)存區(qū)域都有可能發(fā)生oome

2. 怎么判斷OOME出現(xiàn)的區(qū)域

查看異常堆棧信息丛晌,一般在OOME后會(huì)進(jìn)一步跟著提示具體的區(qū)域,比如

Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
 at com.cqx.oom.OOMDemo.lambda$main$0(OOMDemo.java:23)
 at com.cqx.oom.OOMDemo$Lambda$1/668386784.run(Unknown Source)
 at java.lang.Thread.run(Thread.java:748)

3. 什么時(shí)候會(huì)拋出OOME

當(dāng)創(chuàng)建對象時(shí)jvm檢測到內(nèi)存不夠本次內(nèi)存分配澎蛛,于是會(huì)進(jìn)行一次FullGC,如果本次GC后還是內(nèi)存不夠分配呆馁,那就拋出OOME浙滤,當(dāng)前的線程就會(huì)死亡气堕。

OOME Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. ----API文檔

The JVM will run the GC when it's on edge of the OutOfMemoryError. If the GC didn't help at all, then the JVM will throw OOME.

具體情況可能發(fā)生OOME的情況如下

假設(shè)jvm的參數(shù)設(shè)置現(xiàn)在是 -Xmx20M -Xmx20M -Xmn10M -XX:MaxDirectMemorySize=1M, 即分配堆大小為20M,其中老年代和新生代各10M
public static final int _1MB = 1024 * 1024;
1. 持續(xù)不斷創(chuàng)建對象,并不釋放他的引用
for(i = 0; i < 100; i++) {
byte[] M1 = new byte[_1MB];
}
隨著對象不斷創(chuàng)建揖膜,tenured generation到達(dá)了10MB,即已經(jīng)裝滿了次氨,然后繼續(xù)創(chuàng)建對象摘投,隨著新生代中Eden區(qū)再次被填滿,觸發(fā)一次Minor GC幸撕,之前Survivor0中部分對象由于各種原因外臂,比如年齡大了,或者本次MinorGC中發(fā)現(xiàn)S0不夠存放本次存活的對象貌矿,所以會(huì)有對象晉升到Old Generation。此時(shí)JVM發(fā)現(xiàn)老年代的內(nèi)存也不夠進(jìn)行此次分配黑低,于是就進(jìn)行一次FullGC(Major GC)克握,根據(jù)可達(dá)性分析枷踏,從GC ROOTS開始分析對象的引用關(guān)系,發(fā)現(xiàn)任然存活停团,就不進(jìn)行回收下梢,所有本次FullGC并沒有任何卵用,所有GC后還是分配不了,那么此時(shí)當(dāng)前線程就會(huì)拋出一個(gè)OOME番电,然后死亡。

2. byte[] _12MB = new byte[12 * _1MB];
大對象的創(chuàng)建會(huì)根據(jù)JVM參數(shù)的設(shè)置直接分配到老年代中这刷,跟1類似娩井,F(xiàn)ULLGC后發(fā)現(xiàn)內(nèi)存不夠洞辣,拋出OOME

3. ByteBuffer.allocateDirect(2 * _1MB);
由于之前指定了最大直接內(nèi)存為1MB 這邊分配了2MB就拋出了OOME
java.lang.OutOfMemoryError: Direct buffer memory

4. 方法區(qū)OOME
之前在第一次接 Jenkins發(fā)布平臺(tái)時(shí)就出現(xiàn)了這個(gè)問題。當(dāng)時(shí)發(fā)布www定鸟?指定的jdk版本是7著瓶,可能是因?yàn)閣ww大量的jsp文件(感覺也不多),在編譯生成class文件時(shí)導(dǎo)致方法區(qū)不夠用發(fā)生了OOME沸久。看當(dāng)時(shí)打印的異常日志確認(rèn)是方法區(qū)的OOME子刮。
原因: jdk8之前版本不通過-XX:PermSize和-XX:MaxPermSize顯示指定方法區(qū)大小诵竭,應(yīng)該就是64MB卵慰。當(dāng)時(shí)的jenkins中配置只指定了堆大小,沒有指定方法區(qū)的大小
解決: 1. -XX:PermSize和-XX:MaxPermSize 2.改用jdk8(移除了使用永久代來當(dāng)做方法區(qū)的策略病线,使用了metaspace元數(shù)據(jù)區(qū),不暫用jvm指定的堆內(nèi)存鲤嫡,而是使用機(jī)器的native memory,不受jvm堆的限制)

5. 調(diào)用外部服務(wù)惕耕,外部服務(wù)故障或者處理緩慢導(dǎo)致OOME
比如調(diào)用中交興路查詢位置接口诫肠,網(wǎng)絡(luò)或者ZJXL服務(wù)原因請求一直得不到反饋,兩邊處理速度不對等挤安,導(dǎo)致越來越多的請求積壓丧鸯,OOME。
思路:
a. 使用hystrix等熔斷功能的工具來管理外部服務(wù)調(diào)用
b. 復(fù)用tcp連接围肥∨跋龋可以考慮加上keepalive:true的請求頭派敷,避免每次調(diào)用都重新發(fā)起tcp連接撰洗,
c. httpclient這種應(yīng)該是要單例的差导,不需要每次請求都單獨(dú)創(chuàng)建
d. 生產(chǎn)者消費(fèi)者模式來處理

6. 內(nèi)存泄漏
a. 監(jiān)聽器和一些回調(diào)注冊上來但是沒有顯示的取消猪勇,服務(wù)端一直持有這個(gè)監(jiān)聽器的引用。
b. 緩存泄漏助析,比如用HashMap實(shí)現(xiàn)的緩存椅您,吧引用扔到進(jìn)去后忘了,一直扔一直扔就炸了雪隧,GC的時(shí)候發(fā)現(xiàn)對象引用仍然在緩存里员舵,就不回收就炸了∽矗可以用WeakHashMap來實(shí)現(xiàn)韭邓,key是WeakReference,GC可以回收掉。
</pre>

4. OOME會(huì)導(dǎo)致JVM shutdown嗎

不會(huì)可很。OOME只會(huì)把拋出這個(gè)異常的線程給殺掉。 之所以發(fā)生OOME的時(shí)候應(yīng)用程序經(jīng)常出現(xiàn)假死苇本,是因?yàn)镺OM了菜拓,其他正常運(yùn)行的線程也無法分配到資源,各種請求都無法得到處理俺夕,給人一種應(yīng)用掛掉的感覺裳凸。

如果你想在OOME的時(shí)候主動(dòng)kill掉當(dāng)前的應(yīng)用可以
-XX:OnOutOfMemoryError="kill -9 %p"  這里還可以執(zhí)行你的腳本姨谷,這樣就可以自動(dòng)重啟梦湘。件甥。。
%p is the current Java process PID placeholder.

5. 上面說的OOME會(huì)殺死當(dāng)前的線程引有,那么GC可以回收到這部分資源,釋放空間弄捕,那么為什么應(yīng)用程序還是無法響應(yīng)請求呢守谓。

因?yàn)橥鶔伋鯫OME的那個(gè)線程您单,不是大頭,他只是壓垮駱駝的最后一根稻草虐秦。

比如一個(gè)應(yīng)用,堆最大設(shè)為10M蜈彼,一個(gè)定時(shí)任務(wù)的線程讀取數(shù)據(jù)俺驶,在內(nèi)存里生成了上萬個(gè)對象,進(jìn)行處理暮现,此時(shí)用了9.9MB內(nèi)存空間。然后一個(gè)普通的查詢請求進(jìn)來拍顷,OOME了昔案,這個(gè)請求線程死亡了,由于定時(shí)任務(wù)線程還在處理爱沟,GC只把請求線程的資源回收。那么在定時(shí)任務(wù)線程處理期間身冀,還是會(huì)一直出現(xiàn)OOME括享。

6. JVM可以自動(dòng)從OOME恢復(fù)嗎

可以的,但是不建議剩愧。

例如5中的情況娇斩,定時(shí)任務(wù)線程處理完畢了仁卷,那么GC會(huì)吧這部分內(nèi)存釋放掉锦积,程序又正常了歉嗓。 但是發(fā)生OOME的時(shí)候不建議讓JVM自動(dòng)恢復(fù)。因?yàn)槌霈F(xiàn)了這個(gè)異常一定是你程序上有漏洞哮幢,有問題志珍,就算本次恢復(fù)了,接下去很可能還是會(huì)出現(xiàn)這個(gè)問題钢悲。而且OOM恢復(fù)期間很難熬舔株,不管是對于JVM還是用戶來說还棱。具體其他的可以看下面的鏈接

比如之前遇到的,之前做的司機(jī)證件導(dǎo)入功能办铡,就發(fā)生過一次OOME。這個(gè)功能是處理用戶導(dǎo)入的照片文件寡具,服務(wù)器接收這些文件童叠,處理并存放到MongoDB。當(dāng)時(shí)用戶間隔很短時(shí)間導(dǎo)入兩次大約1G的照片文件五垮,第一次導(dǎo)入的請求還在執(zhí)行中杜秸,因?yàn)檎掌枰x取到內(nèi)存,照片文件序列化生成的大對象诞挨,直接進(jìn)入老年代惶傻,MinorGC沒辦法清理,占用了大量的內(nèi)存达罗。緊接著第二次導(dǎo)入請求進(jìn)來了粮揉,在處理過程中由于內(nèi)存不夠OOME了抚笔。但是這并不影響第一次的那個(gè)請求線程。當(dāng)時(shí)jmap -dump:live,format=b,file=path pid dump了堆棧信息辐宾,發(fā)現(xiàn)內(nèi)存夠用啊怎么會(huì)發(fā)生OOME呢膨蛮,覺得很奇怪敞葛。然后我讓用戶再次導(dǎo)入,發(fā)現(xiàn)還是OOME持偏。后面就突然發(fā)現(xiàn)-dump:live或者-histo:live會(huì)先觸發(fā)一次FullGC再進(jìn)行內(nèi)存信息收集,所以出現(xiàn)異常的那個(gè)線程的內(nèi)存都被回收掉了鸿秆,所以dump文件上看來是正常的卿叽。 所以我就讓用戶先別導(dǎo),過個(gè)半個(gè)小時(shí)在來操作附帽,這樣就好了蕉扮。

live指令的解釋
dump only live objects; if not specified, all objects in the heap are dumped.</pre>

7. 怎么應(yīng)對OOME

  1. 考慮在JVM啟動(dòng)參數(shù)就設(shè)置上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath喳钟,但是有風(fēng)險(xiǎn)屁使,比如5中例子奔则,一直會(huì)觸發(fā)OOME,一直會(huì)生成dump文件酬蹋,會(huì)更加麻煩范抓,所以可以考慮加上-XX:OnOutOfMemoryError="kill -9 %p"

  2. 如果1沒做食铐,那么可以考慮用jmap來生成dump堆棧快照迅速保留現(xiàn)場信息象泵,并重啟應(yīng)用(1 2G可能幾分鐘就好了偶惠?洲鸠??扒腕?沒找到數(shù)據(jù),懶得試)萤悴。在此之前可以用jstats查看GC的情況

  3. 如果JVM的分配內(nèi)存很大瘾腰,好幾G,jmap可能需要執(zhí)行很久才生成dump文件覆履,可以考慮用gdb來處理蹋盆,好像會(huì)快很多。不管他不是jdk自帶的工具硝全,要自己下載栖雾。參考鏈接

  4. 考慮使用softReference、weakReference伟众、weakHashMap等等析藕。

  5. 不用jmap,使用編程式方式觸發(fā)生成當(dāng)前的堆轉(zhuǎn)儲(chǔ)快照凳厢,參考鏈接

8. 堆轉(zhuǎn)儲(chǔ)快照分析

工具: jhat/visualVM/JPofiler/MAT 語句: OQL(對象查詢語句)

9. spring actuator/JMX Java Management Extensions

actuator 應(yīng)用監(jiān)控的東西,暴露了一些http接口,有個(gè)事heapdump還有個(gè)stackdump先紫,可以通過定時(shí)任務(wù)之類去訪問然后分析居夹,如果有問題就通知之類的准脂。

jmx 公司有平臺(tái)radar意狠。可以多學(xué)習(xí)下這方面的東西院塞。

10. 死鎖與死循環(huán),CPU飆升相關(guān)排查

  1. jstack 看線程堆棧

  2. top查找cpu占用率高的pid top -p pid -H 查看進(jìn)程內(nèi)線程的pid

  3. pid轉(zhuǎn)16進(jìn)制,去jstack中查日志

https://blog.51cto.com/13732225/2347907

. . . . . .

參考的文檔鏈接

https://stackoverflow.com/questions/3058198/can-the-jvm-recover-from-an-outofmemoryerror-without-a-restart

https://stackoverflow.com/questions/12096403/java-shutting-down-on-out-of-memory-error

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

https://segmentfault.com/a/1190000010603813

https://stackoverflow.com/questions/6418089/does-jmap-force-garbage-collection-when-the-live-option-is-used

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市夸政,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耗帕,老刑警劉巖仿便,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窑业,死亡現(xiàn)場離奇詭異鲤氢,居然都是意外死亡卷玉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衬潦,“玉大人镀岛,你說我怎么就攤上這事驾锰〉揪荩” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵算柳,是天一觀的道長蔗蹋。 經(jīng)常有香客問我仰冠,道長皂吮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮麻裳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己乃摹,他們只是感情好播歼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著烁试,像睡著了一般减响。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颂鸿,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天颖医,我揣著相機(jī)與錄音熔萧,去河邊找鬼佛致。 笑死装哆,一個(gè)胖子當(dāng)著我的面吹牛萍桌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寇损,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了径密?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤于个,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后或链,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年倾贰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岖瑰,到底是詐尸還是另有隱情刻伊,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站键思,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疾党,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一雹洗、第九天 我趴在偏房一處隱蔽的房頂上張望幽勒。 院中可真熱鬧,春花似錦咪惠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吸申。三九已至在跳,卻和暖如春聚凹,著一層夾襖步出監(jiān)牢的瞬間彼哼,已是汗流浹背摩瞎。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工上渴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人次兆。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓渺蒿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子彼妻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容