摘要:直接通過可以造成本機(jī)內(nèi)存溢出。小節(jié)內(nèi)存區(qū)域描述異常程序計(jì)數(shù)器略略略虛擬機(jī)棧存放編譯器可知的各種基本類型,對(duì)象引用和類型每個(gè)線程的棧大小堆存放對(duì)象實(shí)例最大值最小值運(yùn)行時(shí)常亮池存放編譯期生成的字面量和符號(hào)引用,運(yùn)行期也能放入常量池。
堆溢出
Java堆用于存儲(chǔ)對(duì)象實(shí)例,只要不斷地創(chuàng)建對(duì)象,并且保證GC Roots到對(duì)象之間有可達(dá)路徑避免垃圾回收,當(dāng)?shù)竭_(dá)最大堆的容量限制后就會(huì)產(chǎn)生Java.lang.OutOfMemoryError.
/** * VM Options: * -Xms20M * -Xmx20M * -XX:+HeapDumpOnOutOfMemoryError */ public class HeapOOM{ static class OOMObject{} public static void main(String[] args){ Listlist = new ArrayList (); while(true){ list.add(new OOMObject()); } } }
結(jié)果:
GC多次執(zhí)行后觸發(fā)OutOfMemoryError.
關(guān)于虛擬機(jī)棧,在Java規(guī)范中描述了兩種異常:
如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的最大深度,將拋出StackOverflowError異常。
如果虛擬機(jī)在擴(kuò)展棧時(shí)無法申請(qǐng)到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常。
然而,在單線程下,虛擬機(jī)在??臻g不足時(shí)會(huì)嘗試擴(kuò)展??臻g,因此,當(dāng)無法繼續(xù)分配時(shí),到底是內(nèi)存太小,還是已使用的??臻g太大,其實(shí)是一回事。在實(shí)驗(yàn)中,單線程環(huán)境下,只會(huì)拋出StackOverflowError異常。
/** * VM Option: * -Xss160K */ public class JavaVMStackSOF{ private int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args) throws Throwable{ JavaVMStackSOF oom = new JavaVMStackSOF(); try{ oom.stackLeak(); } catch(Throwable e){ System.out.println("Stack length:" + oom.stackLength); throw e; } } }
結(jié)果:
操作系統(tǒng)分配給每個(gè)進(jìn)程的內(nèi)存是有限制的,通常為操作系統(tǒng)限制總內(nèi)存-最大堆容量(Xmx)-最大方法區(qū)容量(MaxPermSize)-程序計(jì)數(shù)器消耗。每個(gè)線程分配到的棧容量越大,可以建立的線程數(shù)目越小。
/** * VM Options: * -Xss2M */ public class JavaVMStackOOM{ private void dontStop(){ while(true){} } public void stackLeakByThread(){ while(true){ Thread thread = new Thread(new Runnable(){ @Override public void run(){ dontStop(); } }); thread.start(); } } public static void main(String[] args) throws Throwable{ JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); } }
結(jié)果:
Exception in thread "main" java.lang.outOfMemoryError: unable to create new native thread
運(yùn)行時(shí)常量池在JDK 1.6及之前版本中在方法區(qū)中,在1.7及之后轉(zhuǎn)移至堆空間。在JDK 1.6及之前版本中可以通過限制方法區(qū)大小,從而間接限制運(yùn)行時(shí)常量池大小。
/** * ONLY WORKS BEFORE JDK 1.7 * VM Options: * -XX:PermSize=10M * -XX:MaxPermSize=10M public class RuntimeConstantPoolOOM{ public static void main(String[] args){ Listlist = new ArrayList (); int i = 0; while(true){ list.add(String.valueof(i++).intern()); } } }
結(jié)果:
Exception in thread "main" java.lang.OutOfMemoryError:PermGen space
方法區(qū)用于存放Class相關(guān)信息,因此要使得方法區(qū)溢出,除了在JDK 1.7之前使運(yùn)行時(shí)常量池溢出外,基本的思路是運(yùn)行時(shí)生成大量的類去填滿方法區(qū)。
結(jié)果
Exception in thread "main" java.lang.OutOfMemoryError:PermGen space
直接內(nèi)存不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分。在JDK 1.4中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,使用Native函數(shù)庫(kù)直接分配堆外內(nèi)存。
DirectMemory容量可通過-XX: MaxDirectMemorySize指定,如果不指定,則默認(rèn)與Java堆最大值一樣(-Xmx)。直接通過allocateMemory可以造成本機(jī)內(nèi)存溢出。
結(jié)果:
Exception in thread "main" java.lang.OutOfMemoryError
直接內(nèi)存溢出的一個(gè)特征是Heap Dump文件中不會(huì)看先明顯的異常指示。如果OOM之后Dump文件很小,而程序中又直接或間接使用了DIO,就應(yīng)該檢查是否直接內(nèi)存溢出。
String.intern()是一個(gè)Native方法,作用是:如果字符串常量池中已經(jīng)包含一個(gè)等于此String對(duì)象的字符串,則返回代表池中這個(gè)字符串的String對(duì)象,否則,將此String對(duì)象包含的字符串添加到常量池中,并且返回此String對(duì)象的引用。
public class RuntimeConstantPoolOOM{ public static void main(String[] args){ String str1 = new StringBuilder("計(jì)算機(jī)").append("軟件").toString(); System.out.println(str1.intern() == str1);//JDK 1.6 false JDK 1.7 true String str2 = new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern() == str2);//JDK 1.6 false JDK 1.7 true } }
在JDK 1.6中,intern()方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代(方法區(qū)運(yùn)行時(shí)常量池),返回的是這個(gè)永久代中這個(gè)字符串實(shí)例的引用,而由StringBuilder創(chuàng)建的字符串實(shí)例在Java堆上,所以必然不是同一個(gè)引用。
在JDK 1.7中,intern()實(shí)現(xiàn)不會(huì)再?gòu)?fù)制,只是在常量池中記錄首次出現(xiàn)的實(shí)例引用,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例是同一個(gè)。
內(nèi)存區(qū)域 | 描述 | VM Option | 異常 |
---|---|---|---|
程序計(jì)數(shù)器 | 略 | 略 | 略 |
虛擬機(jī)棧 | 存放編譯器可知的各種基本類型,對(duì)象引用和returnAddress類型 | -Xss160K 每個(gè)線程的棧大小 | StackOverflowError/OutOfMemoryError |
Java堆 | 存放對(duì)象實(shí)例 | -Xms10M 最大值 -Xmx20M 最小值 |
OutOfMemory: Java heap space |
運(yùn)行時(shí)常亮池 | 存放編譯期生成的字面量和符號(hào)引用,運(yùn)行期也能放入常量池(string.intern())。JDK 1.7之前在方法區(qū)中,JDK 1.7及之后移至堆中 | 隨方法區(qū)或堆設(shè)置 | OutOfMemoryError |
方法區(qū) | 存儲(chǔ)虛擬機(jī)加載的類信息、常亮、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),又稱為永久代(Permanent Generation) | -XX:PermSize=10M 初始值 -XX:MaxPermSize=20M 最大值 |
OutOfMemoryError: PermGen space |
直接內(nèi)存 | 在JDK 1.4中加入NIO類,直接分配堆外內(nèi)存 | -XX:MaxDirectMemorySize=10M, 如果不指定默認(rèn)與-Xmx一樣 |
OutOfMemoryError |
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69402.html
摘要:也正是因此,一旦出現(xiàn)內(nèi)存泄漏或溢出問題,如果不了解的內(nèi)存管理原理,那么將會(huì)對(duì)問題的排查帶來極大的困難。 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbsP9I?w=1024&h=580); 不論做技術(shù)還是做業(yè)務(wù),對(duì)于Java開發(fā)人員來講,理解JVM各種原理的重要性不必再多言 對(duì)于C/C++而言,可以輕易地操作任意地址的...
摘要:內(nèi)存溢出分配的內(nèi)存空間超過系統(tǒng)內(nèi)存。內(nèi)存泄漏的原因分析由大塊組成堆,棧,本地方法棧,程序計(jì)數(shù)器,方法區(qū)。內(nèi)存溢出的原因分析內(nèi)存溢出是由于沒被引用的對(duì)象垃圾過多造成沒有及時(shí)回收,造成的內(nèi)存溢出。小結(jié)棧內(nèi)存溢出程序所要求的棧深度過大導(dǎo)致。 showImg(https://segmentfault.com/img/bVbweuq?w=563&h=300); 前言:JVM中除了程序計(jì)數(shù)器,其他...
摘要:內(nèi)存模型和運(yùn)行時(shí)數(shù)據(jù)區(qū)域的關(guān)系主內(nèi)存對(duì)應(yīng)著堆,工作內(nèi)存對(duì)應(yīng)著棧。在的單例模式中有運(yùn)用到二運(yùn)行時(shí)數(shù)據(jù)區(qū)域內(nèi)存區(qū)域因?yàn)榈倪\(yùn)行時(shí)數(shù)據(jù)區(qū)域一直在改善,所以不同版本之間會(huì)有不同。 一、java內(nèi)存模型 showImg(https://segmentfault.com/img/remote/1460000016694250?w=1810&h=941); java定義內(nèi)存模型的目的是:為了屏蔽各種...
摘要:一內(nèi)存區(qū)域虛擬機(jī)在運(yùn)行時(shí),會(huì)把內(nèi)存空間分為若干個(gè)區(qū)域,根據(jù)虛擬機(jī)規(guī)范版的規(guī)定,虛擬機(jī)所管理的內(nèi)存區(qū)域分為如下部分方法區(qū)堆內(nèi)存虛擬機(jī)棧本地方法棧程序計(jì)數(shù)器。前言 在JVM的管控下,Java程序員不再需要管理內(nèi)存的分配與釋放,這和在C和C++的世界是完全不一樣的。所以,在JVM的幫助下,Java程序員很少會(huì)關(guān)注內(nèi)存泄露和內(nèi)存溢出的問題。但是,一旦JVM發(fā)生這些情況的時(shí)候,如果你不清楚JVM內(nèi)存的...
摘要:內(nèi)存溢出的情況就是從類加載器加載的時(shí)候開始出現(xiàn)的,內(nèi)存溢出分為兩大類和。以下舉出個(gè)內(nèi)存溢出的情況,并通過實(shí)例代碼的方式講解了是如何出現(xiàn)內(nèi)存溢出的。內(nèi)存溢出問題描述元空間的溢出,系統(tǒng)會(huì)拋出。這樣就會(huì)造成棧的內(nèi)存溢出。 導(dǎo)言: 對(duì)于java程序員來說,在虛擬機(jī)自動(dòng)內(nèi)存管理機(jī)制的幫助下,不需要自己實(shí)現(xiàn)釋放內(nèi)存,不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出的問題,由虛擬機(jī)管理內(nèi)存這一切看起來非常美好,但是一旦...
閱讀 856·2023-04-25 21:21
閱讀 3237·2021-11-24 09:39
閱讀 3079·2021-09-02 15:41
閱讀 2009·2021-08-26 14:13
閱讀 1838·2019-08-30 11:18
閱讀 2786·2019-08-29 16:25
閱讀 517·2019-08-28 18:27
閱讀 1590·2019-08-28 18:17