標籤:jvm記憶體 記憶體溢出
我把Java的記憶體地區畫了一張思維導圖,以及各地區的主要功能。
類比Java堆溢出
Java堆用於儲存物件執行個體,只要不斷地建立對象並且保證GC ROOTS到對象之間有可達路徑避免被回收機制清除,就可以類比出Java堆溢出。
package hxl.insist.jvm;import java.util.ArrayList;import java.util.List;/** * 下面是JVM Args: * -Xms20m 堆的最小值 -Xmx20m 堆的最小值 (設定為一樣可避免堆自動擴充) * -XX:+HeapDumpOnOutOfMemoryError 當虛擬機器出現記憶體溢出異常時,Dump出當前的堆轉儲快照 * -XX:HeapDumpPath=E:\eclipseworkspace\UnderStandingTheJVM\hprof 設定產生的堆轉儲快照的路徑 * @author hanxl * */public class HeapOutOfMemory { static class StuffObject { } static List<StuffObject> list = new ArrayList<StuffObject>(); public static void main(String[] args) { Thread thread = new Thread(new Runnable() { public void run() { createObj(); } }); thread.start(); } private static void createObj() { while (true) { list.add(new StuffObject()); } }}
用MemoryAnalyzer分析一下堆轉儲快照如:
從根項目到記憶體消耗聚集點的最短路徑,可以很清楚的看到整個引用鏈。
在上面這張圖上,我們可以清楚的看到,這個對象集合中儲存了大量內部類StuffObject 對象的引用,就是它導致的記憶體泄露。
類比Java虛擬機器棧溢出
關於虛擬機器棧,在Java虛擬機器中規範了兩種異常:
- 如果線程請求的棧深度大於虛擬機器所允許的最大深度,將拋出StackOverflowError異常。
- 如果虛擬機器地擴充棧時無法申請到足夠的記憶體空間,則拋出OutOfMemoryError異常。
類比第一種情況:
package hxl.insist.jvm;/** * 下面是JVM Args: * -Xss128k 設定棧容量大小 * @author hanxl */public class JavaVMStackSOF { public void stackLeak() { stackLeak(); } public static void main(String[] args) throws Throwable { new JavaVMStackSOF().stackLeak(); }}
類比第二種情況:
package hxl.insist.jvm;/** * 下面是JVM Args: * -Xss2M 設定棧容量大小 * @author hanxl */public class JavaVMStackOOM { public static void main(String[] args) { new JavaVMStackOOM().threadInvokeMethod(); } public void threadInvokeMethod() { while (true) { Thread thread = new Thread(new Runnable() { @Override public void run() { infiniteLoop(); } }); thread.start(); } } private void infiniteLoop() { while (true) ; }}
比較兩種情況為什麼實現方式不同?
Java虛擬機器棧是線程私人的,它的生命同期與線程相同。我們把虛擬機器棧比做一個盒子,-Xss是設定盒子的大小,而一個線程只能對應一個盒子。而每個Java方法在執行的時候都會在盒子中建立一個棧幀用於儲存局部變數表等一些資訊。
所以為了製造出第一種情況下的異常,我們把盒子的大小設定小一點,使用遞迴不斷調用方法,從而撐破盒子;而為了製造出第二種情況下的異常,我們應該把盒子的大小設定小大一點,多建立一些盒子,從而讓其無法申請到足夠的記憶體空間。
只要瞭解上面那張思維導圖的記憶體地區,類比出其它記憶體地區異常也很簡單。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java記憶體地區與類比記憶體地區異常