淺析Java OutOfMemoryError

在日常中我們經(jīng)常遇到這樣的錯(cuò)誤:java.lang.OutOfMemoryError: Java heap space。
但是除了heap space 的OutOfMemoryError,還有其它幾種OutOfMemoryError情況奏属。今天我們就來了解一下:
1其馏、java.lang.OutOfMemoryError: Java heap space夭苗。
這是因?yàn)樘摂M機(jī)堆的空間所剩不多缀遍。當(dāng)準(zhǔn)備創(chuàng)建的對(duì)象需要的內(nèi)存已經(jīng)超過虛擬機(jī)堆所剩的空間筷弦。虛擬機(jī)會(huì)嘗試通過full GC來回收內(nèi)存溅潜,如果不行的話,就會(huì)拋出OutOfMemoryError。
導(dǎo)致OutOfMemoryError異常的常見原因有以下幾種:
內(nèi)存中加載的數(shù)據(jù)量過于龐大弧械,如一次性從DB取出過多數(shù)據(jù)蜕乡;
集合類中有對(duì)象的引用,使用完后未清空设捐,使得JVM不能回收借浊;
代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對(duì)象實(shí)體;
啟動(dòng)參數(shù)內(nèi)存值設(shè)定的過小萝招。

舉一個(gè)常見的OutOfMemoryError場(chǎng)景:先從DB讀取數(shù)據(jù)存放到內(nèi)存中蚂斤,然后遍歷處理。

 public class HeapSpaceOutOfMemory {

  private static List<byte[]> getDataFromDb() {
    List<byte[]> list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        byte[] data = new byte[1024 * 1024];// 1M的對(duì)象
        list.add(data);
    }
    return list;
  }

  public static void main(String[] args) {
    // 1即寒、從db取數(shù)據(jù)橡淆,大小為500M
    List<byte[]> list = getDataFromDb();
    // 2、遍歷處理list
    for (byte[] data : list) {
        //do something
    }
    System.out.println("success");
  }
}

JVM參數(shù): -Xms64M -Xmx128M -XX:+PrintGCDetails -Xloggc:/apps/logs/heap_demo.log


image.png

運(yùn)行結(jié)果:


image.png

查看GC Log母赵,可以看到有Full GC 的痕跡逸爵,但是Full GC的收效果不明顯,年輕代和年老代都沒有足夠的空間為即將創(chuàng)建的對(duì)象分配空間凹嘲。
image.png

解決OOM最快的方法就是調(diào)整-Xmx參數(shù)师倔,增加堆的大小。
調(diào)整JVM參數(shù): -Xms64M -Xmx1024M -XX:+PrintGCDetails -Xloggc:/apps/logs/heap_demo.log


image.png

image.png

雖然通過調(diào)整-Xmx參數(shù)解決了上面OutOfMemoryError的問題周蹭,但是如果DB的數(shù)據(jù)突然暴漲到5G趋艘、50G疲恢、500G的時(shí)候,還是會(huì)出現(xiàn)OutOfMemoryError瓷胧。此時(shí)通過調(diào)整-Xmx參數(shù)就不合適了显拳,先不說機(jī)器有沒有這么大的內(nèi)存分配,就算機(jī)器的內(nèi)存夠分配搓萧,F(xiàn)ull GC導(dǎo)致程序停頓的時(shí)間也會(huì)很長(zhǎng)杂数。因此我們要從代碼下手,分批次處理數(shù)據(jù)瘸洛,“小步快跑”揍移,將5G的數(shù)據(jù)拆分成10個(gè)批次,50G的數(shù)據(jù)拆分成100個(gè)批次反肋,500G的數(shù)據(jù)拆分成1000個(gè)批次那伐,每個(gè)批次處理500M的數(shù)據(jù),一個(gè)批次處理完后內(nèi)存回收石蔗。這樣的話就不用再擔(dān)心突然暴漲的數(shù)據(jù)量導(dǎo)致程序OutOfMemoryError罕邀。

2、java.lang.OutOfMemoryError: Metaspace
JDK1.7中抓督,存儲(chǔ)在永久代的部分?jǐn)?shù)據(jù)就已經(jīng)轉(zhuǎn)移到了Java Heap或者是 Native Heap燃少,譬如符號(hào)引用(Symbols)轉(zhuǎn)移到了native heap;字面量(interned strings)轉(zhuǎn)移到了java heap铃在;類的靜態(tài)變量(class statics)轉(zhuǎn)移到了java heap阵具。但永久代仍存在于JDK1.7中,并沒完全移除定铜。
JDK 8.HotSpot JVM使用本地化的內(nèi)存存放類的元數(shù)據(jù)阳液,這個(gè)空間叫做元空間(Metaspace)。官方定義:"In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace"揣炕。元空間的本質(zhì)和永久代類似帘皿,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中畸陡,而是使用本地內(nèi)存鹰溜。因此,默認(rèn)情況下丁恭,元空間的大小僅受本地內(nèi)存限制曹动,但可以通過以下參數(shù)來指定元空間的大小:-XX:MetaspaceSize牲览、-XX:MaxMetaspaceSize墓陈。
測(cè)試代碼:

public class MetaspaceOutOfMemory {

  public static void main(String[] args) throws Exception {
    ClassPool cp = ClassPool.getDefault();
    for (int i = 0;; i++) {
        cp.makeClass("com.demo.MetaspaceClass" + i).toClass();
        Thread.sleep(1);
    }
  }
}

JVM參數(shù):-XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=64m -Xloggc:/apps/logs/heap_demo.log
打開VisualVm工具,可以發(fā)現(xiàn)Metaspace使用的空間大小隨類裝入的數(shù)量增加而增加,這也說明了Metaspace是用來存放類的元數(shù)據(jù)的贡必。


image.png
image.png

image.png

3兔港、java.lang.OutOfMemoryError: PermGen space
修改JVM參數(shù)為:-XX:PermSize=32M -XX:MaxPermSize=64M -Xloggc:/apps/logs/heap_demo.log,切換到JDK7下運(yùn)行MetaspaceOutOfMemory仔拟。打開VisualVm工具衫樊,此時(shí)出現(xiàn)了PermGen標(biāo)簽頁(yè)。隨著類裝載的數(shù)量增加理逊,最終出現(xiàn)了java.lang.OutOfMemoryError: PermGen space橡伞,進(jìn)程退出。


image.png

image.png

image.png

4晋被、java.lang.OutOfMemoryError: GC overhead limit exceeded
GC overhead limt exceed檢查是Hotspot VM 1.6定義的一個(gè)策略,通過統(tǒng)計(jì)GC時(shí)間來預(yù)測(cè)是否要OOM了刚盈,提前拋出異常羡洛。官方定義是:“并行/并發(fā)回收器在GC回收時(shí)間過長(zhǎng)時(shí)會(huì)拋出OutOfMemroyError。過長(zhǎng)的定義是藕漱,超過98%的時(shí)間用來做GC并且回收了不到2%的堆內(nèi)存"欲侮。JVM默認(rèn)啟動(dòng)的時(shí)候-XX:+UseGCOverheadLimit,即啟用了該特性肋联。
測(cè)試程序:

public class GCOverheadLimit {
  public static void main(String[] args) {
    List list = new ArrayList();
    String str = "1";
    for (int i = 0; i < 1000000; i++) {
        for (int j = 0; j < 100; j++) {
            str += "$" + i;
        }
        list.add(str);
    }
  }
}

JVM參數(shù):-Xms64M -Xmx128M -XX:+UseGCOverheadLimit
運(yùn)行結(jié)果:


image.png

最近項(xiàng)目有一個(gè)程序威蕉,因?yàn)轭l繁Full GC導(dǎo)致程序僵死現(xiàn)象,一致耗著橄仍,如果加上-XX:+UseGCOverheadLimit參數(shù)就可以讓程序提前退出韧涨,避免僵死程序長(zhǎng)期占用資源。

5侮繁、java.lang.OutOfMemoryError: unable to create new native thread
如果JVM正在請(qǐng)求操作系統(tǒng)創(chuàng)建一個(gè)本地線程虑粥,而操作系統(tǒng)無法創(chuàng)建的時(shí)候,就會(huì)出現(xiàn)這個(gè)報(bào)錯(cuò)信息宪哩。JVM中可以生成的最大線程數(shù)量由JVM的堆內(nèi)存大小娩贷、Thread的Stack內(nèi)存大小、系統(tǒng)最大可創(chuàng)建的線程數(shù)量(Java線程的實(shí)現(xiàn)是基于底層系統(tǒng)的線程機(jī)制來實(shí)現(xiàn)的锁孟,Windows下_beginthreadex彬祖,Linux下pthread_create)三個(gè)方面影響。
6品抽、java.lang.OutOfMemoryError: Requested array size exceeds VM limit
當(dāng)你正準(zhǔn)備創(chuàng)建一個(gè)超過虛擬機(jī)允許的大小的數(shù)組時(shí)储笑,這條錯(cuò)誤就會(huì)出現(xiàn)在你眼前。64位的操作系統(tǒng)上桑包,JDK7南蓬,如果數(shù)組的長(zhǎng)度是Integer.MAX_VALUE-1,就會(huì)出現(xiàn)。

 byte a[] = new byte[Integer.MAX_VALUE-1];

7、java.lang.OutOfMemoryError: request bytes for . Out of swap space赘方?
這個(gè)錯(cuò)誤是當(dāng)虛擬機(jī)向本地操作系統(tǒng)申請(qǐng)內(nèi)存失敗時(shí)拋出的烧颖。這和你用完了堆或者持久化中的內(nèi)存的情況有些不同。這個(gè)錯(cuò)誤通常是在你的程序已經(jīng)逼近平臺(tái)限制的時(shí)候產(chǎn)生的窄陡。這個(gè)信息告訴你的是你可能已經(jīng)用光了物理內(nèi)存以及虛擬內(nèi)存了炕淮。由于虛擬內(nèi)存通常是用磁盤作為交換分區(qū),因此你最先想到的解決方法可能是先增加交換分區(qū)的大小跳夭。不過我從沒見過一個(gè)程序在頻繁進(jìn)行內(nèi)存交換還能正常運(yùn)行的涂圆,所以這個(gè)方法可能不會(huì)起到什么作用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末币叹,一起剝皮案震驚了整個(gè)濱河市润歉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌颈抚,老刑警劉巖踩衩,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贩汉,居然都是意外死亡驱富,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門匹舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來褐鸥,“玉大人,你說我怎么就攤上這事赐稽〗虚牛” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵又憨,是天一觀的道長(zhǎng)翠霍。 經(jīng)常有香客問我,道長(zhǎng)蠢莺,這世上最難降的妖魔是什么寒匙? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮躏将,結(jié)果婚禮上锄弱,老公的妹妹穿的比我還像新娘。我一直安慰自己祸憋,他們只是感情好会宪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蚯窥,像睡著了一般掸鹅。 火紅的嫁衣襯著肌膚如雪塞帐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天巍沙,我揣著相機(jī)與錄音葵姥,去河邊找鬼。 笑死句携,一個(gè)胖子當(dāng)著我的面吹牛榔幸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矮嫉,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼削咆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蠢笋?” 一聲冷哼從身側(cè)響起拨齐,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挺尿,沒想到半個(gè)月后奏黑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡编矾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馁害。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窄俏。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碘菜,靈堂內(nèi)的尸體忽然破棺而出凹蜈,到底是詐尸還是另有隱情,我是刑警寧澤忍啸,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布仰坦,位于F島的核電站,受9級(jí)特大地震影響计雌,放射性物質(zhì)發(fā)生泄漏悄晃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一凿滤、第九天 我趴在偏房一處隱蔽的房頂上張望妈橄。 院中可真熱鬧,春花似錦翁脆、人聲如沸眷蚓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沙热。三九已至叉钥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篙贸,已是汗流浹背投队。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歉秫,地道東北人蛾洛。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像雁芙,于是被迫代替她去往敵國(guó)和親轧膘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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