1. Java heap space

1.1 java.lang.OutOfMemoryError: Java heap space 概述

Java 應(yīng)用只允許使用有限的內(nèi)存人断。這個(gè)限制是在應(yīng)用啟動(dòng)的時(shí)候指定的。展開(kāi)來(lái)說(shuō), Java內(nèi)存分成2個(gè)不同的區(qū)域。這兩個(gè)區(qū)域叫做Heap Space (堆內(nèi)存)和 Permgen (Permanent Generation部服,即永久代)。

[圖片上傳失敗...(image-f5ee75-1517724409969)]

這兩個(gè)區(qū)的大小是在JVM啟動(dòng)的時(shí)候設(shè)置, 可以通過(guò)JVM參數(shù)-Xmx-XX:MaxPermSize進(jìn)行設(shè)置. 如果你沒(méi)歐進(jìn)行特別的設(shè)置, 平臺(tái)指定的默認(rèn)配置會(huì)被使用.
java.lang.OutOfMemoryError: Java heap space 錯(cuò)誤會(huì)在應(yīng)用嘗試添加更多的數(shù)據(jù)到heap space, 但是heap區(qū)沒(méi)有足夠的空間時(shí)觸發(fā).

需要注意的是即使物理內(nèi)存可能有很多剩余, 但是只要JVM達(dá)到了heap size的限制, 就會(huì)拋出該錯(cuò)誤.

1.2 原因

對(duì)于 java.lang.OutOfMemoryError: Java heap space , 最常見(jiàn)的原因很簡(jiǎn)單 -- 你把一個(gè)XXL號(hào)的應(yīng)用放到了一個(gè)S號(hào)的Java heap space里了. 也就是說(shuō) -- 應(yīng)用需要更多的Java heap space 來(lái)讓它正常運(yùn)行. 對(duì)于這個(gè)OutOfMemory, 其他的原因會(huì)更復(fù)雜, 通常是由于編程錯(cuò)誤引起的:

  • 用戶/數(shù)據(jù)量出現(xiàn)峰值 該應(yīng)用被設(shè)計(jì)來(lái)處理一定數(shù)量的用戶和一定數(shù)量的數(shù)據(jù). 當(dāng)用戶數(shù)或數(shù)據(jù)量突然沖高, 并且超過(guò)了期望的閾值, 在出現(xiàn)峰值停止之前的正常運(yùn)行時(shí)的操作觸發(fā)了 java.lang.OutOfMemoryError: Java heap space 錯(cuò)誤.
  • 內(nèi)存泄漏 一種特定類型的編程錯(cuò)誤導(dǎo)致應(yīng)用頻繁消耗更多的內(nèi)存. 每當(dāng)應(yīng)用的泄漏的功能被使用時(shí), 它就會(huì)在Java heap space種生成一些對(duì)象. 隨著時(shí)間推移, 泄漏的對(duì)象消耗了所有可用的Java heap space, 并且觸發(fā)了常見(jiàn)的java.lang.OutOfMemoryError: Java heap space 錯(cuò)誤.

1.3 示例

1.3.1 示例1

第一個(gè)例子相當(dāng)簡(jiǎn)單 -- 下列的Java 代碼嘗試分配200萬(wàn)個(gè)(2M) 整數(shù)數(shù)組. 當(dāng)你編譯該代碼, 用一個(gè)12MB大小的Java heap space (java -Xmx12m OOM)運(yùn)行. 它會(huì)運(yùn)行失敗, 拋出 java.lang.OutOfMemoryError: Java heap space 消息. 有13MB Java heap space, 這個(gè)程序就能正常運(yùn)行...

class OOM {
  static final int SIZE=2*1024*1024;
  public static void main (String[] a) {
    int[] i = new int[SIZE]
  }
}

1.3.2 內(nèi)存泄漏示例

第二個(gè), 更現(xiàn)實(shí)一點(diǎn)的例子是內(nèi)存泄漏. 在Java里, 當(dāng)開(kāi)發(fā)創(chuàng)建和使用新對(duì)象, 如: new Integer(5), 他們不必自己分派內(nèi)存 -- 這通過(guò)JVM來(lái)處理. 在應(yīng)用生命周期種, JVM會(huì)周期性地檢查內(nèi)存中的哪個(gè)對(duì)象仍在使用, 哪個(gè)沒(méi)有. 沒(méi)有被使用的對(duì)象會(huì)被丟棄, 然后內(nèi)存重新聲明并重新使用. 這個(gè)過(guò)程叫做垃圾回收. 對(duì)應(yīng)的JVM里的模塊叫做垃圾收集器.

Java的自動(dòng)內(nèi)存管理機(jī)制以來(lái)與GC來(lái)周期性地查找沒(méi)用的對(duì)象并移除他們. 簡(jiǎn)而言之, Java內(nèi)存泄漏是這么一種場(chǎng)景, 一些對(duì)象應(yīng)用已經(jīng)不用了, 但是GC卻沒(méi)有檢查出來(lái). 結(jié)果就是這些沒(méi)用的對(duì)象仍然無(wú)限期地存在在Java heap space 中. 如此往復(fù), 最終觸發(fā)java.lang.OutOfMemoryError: Java heap space錯(cuò)誤.

構(gòu)造一個(gè)滿足內(nèi)存泄漏定義的Java程序也相當(dāng)容易:

class KeylessEntry {
  static class Key {
    Integer id;
 
    Key(Integer id) {
      this.id = id;
    }
  @Override
    public int hashCode() {
      return id.hashCode();
    }
  }
  public static void main(String[] args) {
    Map m = new HashMap();
    while (true)
      for (int i=0; i<10000, i++)
        if (!m.containsKey(new Key(i)))
          m.put(new Key(i), "Nmber:" + i);
  }
}

當(dāng)執(zhí)行上面的代碼時(shí)拗慨,您可能期望它永遠(yuǎn)運(yùn)行而沒(méi)有任何問(wèn)題廓八,假設(shè)原始緩存解決方案只將Map擴(kuò)展到10,000個(gè)元素,除此之外赵抢,HashMap中已經(jīng)包含了所有鍵. 然而, 事實(shí)上元素會(huì)持續(xù)增加因?yàn)镵ey這個(gè)類沒(méi)有在它的hashCode()種包含一個(gè)適當(dāng)?shù)?code>equals()實(shí)現(xiàn).

結(jié)果, 隨著時(shí)間推移, 因?yàn)樾孤┐a的不斷的使用, "緩存"的結(jié)果會(huì)消耗大量的Java heap space. 當(dāng)泄漏的內(nèi)存填滿了heap區(qū)的所有的可用內(nèi)存, 并且垃圾收集器無(wú)法清理, 會(huì)拋出java.lang.OutOfMemoryError: Java heap space.

解決辦法也簡(jiǎn)單 -- 添加個(gè)equals()方法的實(shí)現(xiàn)在下邊, 就能很好的運(yùn)行了. 但是在你最終找到這個(gè)bug之前, 你會(huì)西歐愛(ài)好相當(dāng)多的腦細(xì)胞.

@Override
public boolean equals(Object o) {
  boolean response = false;
  if (o instanceof Key) {
    response = (((Key)o).id).equals(this.id);
  }
  return response;
}

1.4 解決方案

顯然第一個(gè)解決方案就是 -- 當(dāng)你的JVM特定資源耗盡了, 你應(yīng)該增加那個(gè)資源的量. 在這個(gè)案例種: 當(dāng)你的應(yīng)用沒(méi)有足夠的Java heap space內(nèi)存來(lái)正常運(yùn)行, 只需要在運(yùn)行JVM的時(shí)候配置并添加(或修改現(xiàn)有的)如下參數(shù):
-Xmx1024m

上述配置會(huì)給應(yīng)用1024M的Java heap space. 你可以使用g或者G(單位是GB), mM(MB), kK(KB). 例如下列都是設(shè)置最大Java heap space為1GB:

java -Xmx1073741824 com.mycompany.MyClass
java -Xmx1048576k com.mycompany.MyClass
java -Xmx1024m com.mycompany.MyClass
java -Xmx1g com.mycompany.MyClass

然而, 在很多案例種, 提供更多的Java heap space只是飲鴆止渴. 例如, 如果你的應(yīng)用存在內(nèi)存泄漏, 添加更多的heap只是延緩java.lang.OutOfMemoryError: Java heap space錯(cuò)誤的出現(xiàn), 并不能解決問(wèn)題. 另外, 增加Java heap space也會(huì)導(dǎo)致GC暫停時(shí)間的增加, 從而影響你的應(yīng)用的吞吐量和延遲.

如果你希望解決潛在的問(wèn)題, 而不是頭痛醫(yī)頭, 聯(lián)系我就是最好的方式(@ ̄ー ̄@). 當(dāng)然, 有幾個(gè)工具適合你. Debuggers, profiles, heap dump analyzers -- 供你選擇.

題外話:
Dynatrace 也是個(gè)分析OOM問(wèn)題的好工具.感興趣的可以參考這篇文章:
《案例: Dynatrace分析某財(cái)險(xiǎn)承保系統(tǒng)內(nèi)存泄漏問(wèn)題》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剧蹂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子烦却,更是在濱河造成了極大的恐慌宠叼,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異冒冬,居然都是意外死亡伸蚯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門简烤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)剂邮,“玉大人,你說(shuō)我怎么就攤上這事横侦』用龋” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵枉侧,是天一觀的道長(zhǎng)引瀑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)榨馁,這世上最難降的妖魔是什么憨栽? 我笑而不...
    開(kāi)封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮辆影,結(jié)果婚禮上徒像,老公的妹妹穿的比我還像新娘黍特。我一直安慰自己蛙讥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布灭衷。 她就那樣靜靜地躺著次慢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翔曲。 梳的紋絲不亂的頭發(fā)上迫像,一...
    開(kāi)封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音瞳遍,去河邊找鬼闻妓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掠械,可吹牛的內(nèi)容都是我干的由缆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼猾蒂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼均唉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起肚菠,我...
    開(kāi)封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤舔箭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蚊逢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體层扶,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箫章,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镜会。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炉抒。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖稚叹,靈堂內(nèi)的尸體忽然破棺而出焰薄,到底是詐尸還是另有隱情,我是刑警寧澤扒袖,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布塞茅,位于F島的核電站,受9級(jí)特大地震影響季率,放射性物質(zhì)發(fā)生泄漏野瘦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一飒泻、第九天 我趴在偏房一處隱蔽的房頂上張望鞭光。 院中可真熱鬧,春花似錦泞遗、人聲如沸惰许。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汹买。三九已至,卻和暖如春聊倔,著一層夾襖步出監(jiān)牢的瞬間晦毙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工耙蔑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留见妒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓甸陌,卻偏偏與公主長(zhǎng)得像须揣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子邀层,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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