概述
OOM:
- JVM一般都是先嘗試GC,GC以后仍然無法騰出空間給新對象的時候才會針對對應線程觸發(fā)OOM
- 另外OOM只是一個求救信號咧叭,JVM的一種自我保護機制蚀乔,會自動停止導致異常的線程,但是不會導致整個應用的崩潰
- 觸發(fā)OOM的原因:池子太小菲茬,對象太大(加載的東西太多吉挣、單個對象太大、無法及時釋放資源)
- OOM的解決方案:(Perm婉弹、Heap) check內存配置睬魂、根據報錯信息 或者 dump快照定位大對象的來源
OOM觸發(fā)的幾種情況:
- Heap不夠:java.lang.OutOfMemoryError: Java heap space :設置 -Xms 來解決
- Perm不夠:java.lang.OutOfMemoryError: PermGen space:加載了太多的類或者常量池太大了:設置-XX:MaxPermSize
- 棧區(qū)不夠:(StackOverFlowError、java.lang.OutOfMemoryError: unable to create new native thread)
觸發(fā)FGC的方法:
- 主動觸發(fā):System.gc()镀赌、命令觸發(fā)(jmap -dump:live)氯哮、工具觸發(fā)(jconsol)
- 老年代內存不足:真不足、預判不足佩脊、內存碎片蛙粘、CMS 晉升失敗、配置太小
- Perm區(qū)內存不足:加載太多的類威彰、配置太小
OOM的排查
- 導致OutOfMemoryError異常的常見原因有以下幾種:
- 內存中加載的數據量過于龐大出牧,如一次從數據庫取出過多數據,取了又無法釋放歇盼,不能GC
- 集合類中有對對象的引用舔痕,使用完后未清空,使得JVM不能回收豹缀;
- 代碼中存在死循環(huán)或循環(huán)產生過多重復的對象實體伯复;
- 使用的第三方軟件中的BUG;
- 啟動參數內存值設定的過小
- OutOfMemoryError: PermGen space
- 問題分析: 加載了太多的類邢笙,比如第三方jar包啸如。或者加載了太多的JSP氮惯。
- 解決方案: 增加java虛擬機中的XX:PermSize和XX:MaxPermSize參數的大小
- OutOfMemoryError: Java heap space
- 檢查程序叮雳,看是否有死循環(huán)或不必要地重復創(chuàng)建大量對象想暗。找到原因后,修改程序和算法帘不。
- 增加Java虛擬機中Xms(初始堆大兴的)和Xmx(最大堆大小)參數的大小寞焙。如:set JAVA_OPTS= -Xms256m -Xmx1024m
- 常見代碼引起的OOM的情況
- 檢查代碼中是否有死循環(huán)或遞歸調用储狭。
- 檢查是否有大循環(huán)重復產生新對象實體。
- 檢查對數據庫查詢中捣郊,是否有一次獲得全部數據的查詢辽狈。一般來說,如果一次取十萬條記錄到內存呛牲,就可能引起內存溢出稻艰。這個問題比較隱蔽,在上線前侈净,數據庫中數據較少,不容易出問題僧凤,上線后畜侦,數據庫中數據多了,一次查詢就有可能引起內存溢出躯保。因此對于數據庫查詢盡量采用分頁的方式查詢旋膳。
- 檢查List、MAP等集合對象是否有使用完后途事,未清除的問題验懊。List、MAP等集合對象會始終存有對對象的引用尸变,使得這些對象不能被GC回收义图。
常用JVM異常排查手段
- 監(jiān)控工具的使用,自帶的JConsole召烂、visualVM的使用
- 圖形化顯示 堆區(qū)內存變化碱工、線程數、CPU使用等情況
- visualVM可以直接生成內存快照奏夫、線程快照怕篷,也可以直接幫你計算最大的20個Object
- 常用命令:
- jmap (-heap 查看堆區(qū)的配置信息)
- jstat(gcutil看即時的各個區(qū)域的變化情況、capacity查看容量情況)
gc日志分析(啟動的時候設置參數酗昼,生成gc日志廊谓,然后用工具分析gc日志,比如FGC的次數麻削、YGC的次數等信息)
快照分析(可以通過工具蒸痹、命令春弥、或者啟動參數配置獲取到快照,然后利用工具分析)
FullGC觸發(fā)的情況
- 老年代空間不足:
- 真不足电抚,新生代出來的對象進入老年代惕稻,而老年代的剩余空間不足
- 預判不足,統計得到的Minor GC晉升到舊生代的平均大小大于舊生代的剩余空間
- 連續(xù)不足蝙叛,如果是CMS的話俺祠,即使內存剩余夠。但是內存碎片太多借帘,沒有連續(xù)的內存裝下原本可以裝下的對象
- 緊急不足蜘渣,CMS在Major GC的時候,因為是并發(fā)清除肺然,未完成MGC時新垃圾產生蔫缸,而剩余空間不足,也會觸發(fā)FullGC际起,而且是以Serial Old單線程的形式執(zhí)行拾碌。CMS的Major GC約等于FullGC的,因為一般都是YGC產生垃圾要進入老年代街望,而老年代又不足的時候才會觸發(fā)MajorGC校翔,這就是一個完整的FullGC啊。但是也不排除設置了大對象直接進入老年代灾前,這樣的話防症,可能沒有YGC,就直接進入老年代觸發(fā)MajorGC了
解決方案:調優(yōu)時應盡量做到讓對象在Minor GC階段被回收
- 調大堆區(qū)的大小哎甲,默認MaxSize是總內存的1/4蔫敲,初始值是1/64
- 代碼里面,盡量不要有太大的對象產生炭玫,可以用分批讀取奈嘿,讓該對象不直接進入老年代,而是被YGC回收掉
- 調大年輕代的大小
- 調大存活ratio吞加,默認是15
Perm區(qū)滿了:解決方案:調大Perm區(qū)的大小指么,或者代碼中不要加載過多的class
代碼調用 System.gc()×穸Γ可以設置jvm參數伯诬,禁止程序調用這個方法。
jmap -histo:live pid 暴力GC巫财,直接用命令
CMS的GC非常特殊盗似,其它的SerialOld和Parallel Old回收器在老年代滿了都是進行Full GC,而唯獨CMS不是這樣平项,CMS會定時檢測老年代的占用比例赫舒,超過一定的比例就會觸發(fā)老年代的GC悍及。