JVM內(nèi)存溢出詳解(棧溢出,堆溢出狰住,持久代溢出张吉、無法創(chuàng)建本地線程)

轉(zhuǎn)載:http://www.reibang.com/p/cd705f88cf2a


1、內(nèi)存溢出和內(nèi)存泄漏的區(qū)別

內(nèi)存溢出 (Out Of Memory):是指程序在申請內(nèi)存時催植,沒有足夠的內(nèi)存空間供其使用肮蛹,出現(xiàn)Out Of Memory。

內(nèi)存泄露 (Memory Leak):是指程序在申請內(nèi)存后创南,由于某種原因無法釋放已申請的內(nèi)存空間伦忠,導(dǎo)致這塊內(nèi)存無法再次被利用,造成系統(tǒng)內(nèi)存的浪費(fèi)稿辙。

一次內(nèi)存泄露危害可以忽略昆码,但內(nèi)存泄露堆積的后果很嚴(yán)重,無論多少內(nèi)存邻储,遲早會被占光赋咽。

Memory Leak 會最終會導(dǎo)致 Out Of Memory 。


2吨娜、內(nèi)存溢出分類

2.1 棧內(nèi)存溢出(StackOverflowError):

程序所要求的棧深度過大導(dǎo)致脓匿,可以寫一個死遞歸程序觸發(fā)。

2.2 堆內(nèi)存溢出(OutOfMemoryError : java heap space)

需要分清是 內(nèi)存溢出 還是 內(nèi)存泄漏:
(1)如果是內(nèi)存溢出宦赠,則通過 調(diào)大 -Xms陪毡,-Xmx參數(shù)。
(2)如果是內(nèi)存泄露袱瓮,則看對象如何被 GC Root 引用缤骨。

2.3 持久帶內(nèi)存溢出(OutOfMemoryError: PermGen space)

持久帶中包含方法區(qū),方法區(qū)包含常量池尺借。

因此持久帶溢出有可能是(1) 運(yùn)行時常量池溢出绊起,也有可能是(2)方法區(qū)中保存的Class對象沒有被及時回收掉或者Class信息占用的內(nèi)存超過了我們配置。

用String.intern()觸發(fā)常量池溢出燎斩。
Class對象未被釋放虱歪,Class對象占用信息過多,有過多的Class對象栅表∷癖桑可以導(dǎo)致持久帶內(nèi)存溢出。

2.4 無法創(chuàng)建本地線程

Caused by: java.lang.OutOfMemoryError:unable to create new native thread

系統(tǒng)內(nèi)存的總?cè)萘坎蛔児制浚褍?nèi)存萧落、非堆內(nèi)存設(shè)置過大,會導(dǎo)致能給線程分配的內(nèi)存不足。


3找岖、內(nèi)存溢出詳解

3.1 棧溢出(StackOverflowError)

棧溢出拋出 StackOverflowError 錯誤陨倡,出現(xiàn)此種情況是因為方法運(yùn)行的時候棧的深度超過了虛擬機(jī)容許的最大深度所致。一般情況下是程序錯誤所致的许布,比如寫了一個死遞歸兴革,就有可能造成此種情況。下面我們通過一段代碼來模擬一下此種情況的內(nèi)存溢出蜜唾。

import java.util.*;    
import java.lang.*;    
public class OOMTest{     
    public void stackOverFlowMethod(){    
        stackOverFlowMethod();    
    }    
    public static void main(String[] args){    
        OOMTest oom = new OOMTest();    
        oom.stackOverFlowMethod();    
    }    
}    

運(yùn)行上面的代碼杂曲,會拋出如下的異常:

Exception in thread "main" java.lang.StackOverflowError    
        at OOMTest.stackOverFlowMethod(OOMTest.java:6)   

對于棧內(nèi)存溢出,根據(jù)《Java 虛擬機(jī)規(guī)范》中文版:

如果線程請求的棧容量超過棧允許的最大容量的話袁余,Java 虛擬機(jī)將拋出一個StackOverflow異常擎勘;如果Java虛擬機(jī)棧可以動態(tài)擴(kuò)展颖榜,并且擴(kuò)展的動作已經(jīng)嘗試過货抄,但是無法申請到足夠的內(nèi)存去完成擴(kuò)展,或者在新建立線程的時候沒有足夠的內(nèi)存去創(chuàng)建對應(yīng)的虛擬機(jī)棧朱转,那么Java虛擬機(jī)將拋出一個OutOfMemory 異常蟹地。

3.2 堆溢出(OutOfMemoryError:java heap space)

堆內(nèi)存溢出的時候,虛擬機(jī)會拋出 java.lang.OutOfMemoryError:java heap space藤为。

出現(xiàn)此種情況的時候怪与,我們需要根據(jù)內(nèi)存溢出的時候產(chǎn)生的 dump 文件來具體分析(需要增加 -XX:+HeapDumpOnOutOfMemoryError jvm啟動參數(shù))。出現(xiàn)此種問題的時候有可能是內(nèi)存泄漏缅疟,也有可能是內(nèi)存溢出了分别。

1、配置方法

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目錄}存淫。

2耘斩、參數(shù)說明

(1)-XX:+HeapDumpOnOutOfMemoryError參數(shù)表示當(dāng)JVM發(fā)生OOM時,自動生成DUMP文件桅咆。

(2)-XX:HeapDumpPath=${目錄}參數(shù)表示生成DUMP文件的路徑括授,也可以指定文件名稱,例如:-XX:HeapDumpPath=${目錄}/java_heapdump.hprof岩饼。如果不指定文件名荚虚,默認(rèn)為:java_<pid><date><time>_heapDump.hprof。

如果是內(nèi)存泄漏籍茧,我們要找出內(nèi)存泄漏的對象是怎么被GC ROOT引用起來版述,然后通過引用鏈來具體分析泄露的原因。

如果出現(xiàn)了內(nèi)存溢出問題寞冯,這往往是程序本生需要的內(nèi)存大于了我們給虛擬機(jī)配置的內(nèi)存渴析,這種情況下晚伙,我們可以采用調(diào)大-Xmx來解決這種問題。

下面我們通過如下的代碼來演示一下此種情況的溢出:

import java.util.*;    
import java.lang.*;    
public class OOMTest{    
        public static void main(String[] args){    
                List<byte[]> buffer = new ArrayList<byte[]>();    
                buffer.add(new byte[10*1024*1024]);    
        }    
}   

我們通過如下的命令運(yùn)行上面的代碼:

java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest

程序輸出如下的信息:

[GC 1180K->366K(19456K), 0.0037311 secs]    
[Full GC 366K->330K(19456K), 0.0098740 secs]    
[Full GC 330K->292K(19456K), 0.0090244 secs]    
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space    
        at OOMTest.main(OOMTest.java:7)    

從運(yùn)行結(jié)果可以看出俭茧,JVM進(jìn)行了一次Minor gc和兩次的Major gc撬腾,從Major gc的輸出可以看出,gc以后old區(qū)使用率為134K恢恼,而字節(jié)數(shù)組為10M,加起來大于了old generation(老年代)的空間胰默,所以拋出了異常场斑,如果調(diào)整 -Xms21M,-Xmx21M,那么就不會觸發(fā)gc操作也不會出現(xiàn)異常了牵署。

通過上面的實驗其實也從側(cè)面驗證了一個結(jié)論:對象大于新生代剩余內(nèi)存的時候漏隐,將直接放入老年代,當(dāng)老年代剩余內(nèi)存還是無法放下的時候奴迅,觸發(fā)垃圾收集青责,收集后還是不能放下就會拋出內(nèi)存溢出異常了。

3.3 持久帶溢出(OutOfMemoryError: PermGen space)

我們知道 Hotspot jvm 通過持久帶實現(xiàn)了Java虛擬機(jī)規(guī)范中的方法區(qū)取具,而運(yùn)行時的常量池就是保存在方法區(qū)中脖隶。

因此持久帶溢出有可能是:

(1) 運(yùn)行時常量池溢出
(2)方法區(qū)中保存Class對象沒有被及時回收掉或者Class信息占用的內(nèi)存超過了我們配置暇检。

當(dāng)持久帶溢出的時候拋出 java.lang.OutOfMemoryError: PermGen space产阱。可能在如下幾種場景下出現(xiàn):

  • 使用一些應(yīng)用服務(wù)器的熱部署的時候块仆,我們就會遇到熱部署幾次以后發(fā)現(xiàn)內(nèi)存溢出了构蹬,這種情況就是因為每次熱部署的后,原來的Class沒有被卸載掉悔据。

  • 如果應(yīng)用程序本身比較大庄敛,涉及的類庫比較多,但是我們分配給持久帶的內(nèi)存(通過-XX:PermSize和-XX:MaxPermSize來設(shè)置)比較小的時候也可能出現(xiàn)此種問題科汗。

  • 一些第三方框架藻烤,比如spring、hibernate都是通過字節(jié)碼生成技術(shù)(比如CGLib)來實現(xiàn)一些增強(qiáng)的功能头滔,這種情況可能需要更大的方法區(qū)來存儲動態(tài)生成的Class文件隐绵。

  • 我們知道Java中字符串常量是放在常量池中的,String.intern()這個方法運(yùn)行的時候拙毫,會檢查常量池中是否存和本字符串相等的對象依许,如果存在直接返回常量池中對象的引用,不存在的話缀蹄,先把此字符串加入常量池峭跳,然后再返回字符串的引用膘婶。

那么我們就可以通過String.intern方法來模擬一下運(yùn)行時常量區(qū)的溢出。下面我們通過如下的代碼來模擬此種情況:

import java.util.*;    
import java.lang.*;    
public class OOMTest {    
        public static void main(String[] args) {    
                List<String> list = new ArrayList<String>();    
                while(true){    
                        list.add(UUID.randomUUID().toString().intern());    
                }    
        }        
}    

我們通過如下的命令運(yùn)行上面代碼:

java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest

運(yùn)行后的控制臺輸出如下圖所示:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space    
        at java.lang.String.intern(Native Method)    
        at OOMTest.main(OOMTest.java:8)   

通過上面的代碼蛀醉,我們成功模擬了運(yùn)行時常量池溢出的情況悬襟,從輸出中的PermGen space可以看出確實是持久帶發(fā)生了溢出,這也驗證了拯刁,我們前面說的Hotspot jvm通過持久帶來實現(xiàn)方法區(qū)的說法脊岳。

3.4 無法創(chuàng)建本地線程

java.lang.OutOfMemoryError: unable to create new native thread

最后我們在來看看java.lang.OutOfMemoryError:unable to create new native thread這種錯誤。 出現(xiàn)這種情況的時候垛玻,一般是下面兩種情況導(dǎo)致的:

  • 程序創(chuàng)建的線程數(shù)超過了操作系統(tǒng)的限制割捅。對于Linux系統(tǒng),我們可以通過 ulimit -u 來查看此限制帚桩。

  • 給虛擬機(jī)分配的內(nèi)存過大亿驾,導(dǎo)致創(chuàng)建線程的時候需要的native內(nèi)存太少。

建立每個線程账嚎,都需要給這個線程分配椖玻空間。

我們都知道操作系統(tǒng)對每個進(jìn)程的內(nèi)存是有限制的郭蕉,我們啟動jvm疼邀,相當(dāng)于啟動了一個進(jìn)程,假如我們一個進(jìn)程占用了4G的內(nèi)存召锈,那么通過下面的公式計算出來的剩余內(nèi)存就是建立線程棧的時候可以用的內(nèi)存檩小。

線程棧總可用內(nèi)存 = 4G -(-Xmx的值)- (-XX:MaxPermSize的值)- 程序計數(shù)器占用的內(nèi)存

通過上面的公式我們可以看出烟勋,-Xmx 和 MaxPermSize的值越大规求,那么留給線程棧可用的空間就越小卵惦,在-Xss參數(shù)配置的棧容量不變的情況下阻肿,可以創(chuàng)建的線程數(shù)也就越小。因此如果是因為這種情況導(dǎo)致的unable to create native thread沮尿,那么要么我們增大進(jìn)程所占用的總內(nèi)存丛塌,或者減少-Xmx或者-Xss來達(dá)到創(chuàng)建更多線程的目的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畜疾,一起剝皮案震驚了整個濱河市赴邻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌啡捶,老刑警劉巖姥敛,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞎暑,居然都是意外死亡彤敛,警方通過查閱死者的電腦和手機(jī)与帆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墨榄,“玉大人玄糟,你說我怎么就攤上這事“乐龋” “怎么了阵翎?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長之剧。 經(jīng)常有香客問我郭卫,道長,這世上最難降的妖魔是什么猪狈? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮辩恼,結(jié)果婚禮上雇庙,老公的妹妹穿的比我還像新娘。我一直安慰自己灶伊,他們只是感情好疆前,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著聘萨,像睡著了一般竹椒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上米辐,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天胸完,我揣著相機(jī)與錄音,去河邊找鬼翘贮。 笑死赊窥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狸页。 我是一名探鬼主播锨能,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芍耘!你這毒婦竟也來了址遇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤斋竞,失蹤者是張志新(化名)和其女友劉穎倔约,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坝初,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跺株,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年复濒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乒省。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡巧颈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袖扛,到底是詐尸還是另有隱情砸泛,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蛆封,位于F島的核電站唇礁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惨篱。R本人自食惡果不足惜盏筐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砸讳。 院中可真熱鬧琢融,春花似錦、人聲如沸簿寂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽常遂。三九已至纳令,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間克胳,已是汗流浹背平绩。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漠另,地道東北人馒过。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像酗钞,于是被迫代替她去往敵國和親腹忽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

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

  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個部分砚作,分別是: ...
    光劍書架上的書閱讀 2,483評論 2 26
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理窘奏,因此不免有一些不準(zhǔn)確的地方,同時不同JDK版本的...
    高廣超閱讀 15,545評論 3 83
  • 文/藍(lán)綠小巨人 -01- 我以為葫录,一本書的書名着裹,就像一層美味的糖衣。它總是氤氳著夏露的甜香米同,讓人完全阻擋不了骇扇,只會...
    藍(lán)綠小巨人閱讀 1,773評論 50 70
  • 一位網(wǎng)友特意打來幾個電話摔竿,把自己驗證過的便秘偏方告訴我:中藥店買大黃研末用香油調(diào)勻,每次服用6克少孝,晚上睡覺前開水沖...
    謝氏養(yǎng)生堂閱讀 208評論 0 1
  • 跟老父親聊天继低,談到原來人們的文化水平,才明白自己心目中知識淵博的“老教師”僅僅是當(dāng)時的完小畢業(yè)稍走。但是袁翁,父親說在當(dāng)時...
    像話讀書爻閱讀 916評論 3 8