1. 程式人生 > >Java記憶體區域與模擬記憶體區域異常

Java記憶體區域與模擬記憶體區域異常

我把Java的記憶體區域畫了一張思維導圖,以及各區域的主要功能。

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虛擬機器中規範了兩種異常:

  1. 如果執行緒請求的棧深度大於虛擬機器所允許的最大深度,將丟擲StackOverflowError異常。
  2. 如果虛擬機器地擴充套件棧時無法申請到足夠的記憶體空間,則丟擲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方法在執行的時候都會在盒子中建立一個棧幀用於儲存區域性變量表等一些資訊。

所以為了製造出第一種情況下的異常,我們把盒子的大小設定小一點,使用遞迴不斷呼叫方法,從而撐破盒子;而為了製造出第二種情況下的異常,我們應該把盒子的大小設定小大一點,多建立一些盒子,從而讓其無法申請到足夠的記憶體空間。

只要瞭解上面那張思維導圖的記憶體區域,模擬出其它記憶體區域異常也很簡單。