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