初識java虛擬機(JVM)運行時數據區結構

java虛擬機(JVM)是java平臺的基石,任何java程序都是在JVM上運行的。java編譯器把java程序編譯成class文件,然後把class文件載入JVM中運行,JVM屏蔽了底層的硬件,所有的class文件只在JVM中運行,實現了一次編寫到處運行(Write Once Run Anywhere)的宏偉目標。

JVM內部結構包括:

  1. 類加載子系統
  2. 運行時數據區
  3. 執行引擎

本文只介紹運行時數據區的結構。

 
 

JVM運行時數據區結構

在這裏插入圖片描述

  • 方法區
    虛擬機共享的數據區,每個虛擬機只有一個方法區。主要存儲已被虛擬機加載的類的信息、常量、靜態變量等數據。
  • 堆區
    虛擬機共享的數據區,每個虛擬機只有一個堆區。java 堆是虛擬機管理的內存中最大的一塊區域,存儲所有的對象實例以及數組。由於堆區是多線程共享的,所以不是線程安全的。
  • 棧區
    每個線程都有自己獨立的棧區,棧區是線程安全的,是java方法執行的內存模型。每個方法在執行時都會創建一個棧幀(stack frame)用於存儲本地變量表、操作數棧、幀數據等。隨着方法調用的結束,相應的棧幀數據也隨之刪除。
  • 程序計數器
    每個線程都有自己獨立的程序計數器,指向當前指令的地址。
  • 本地方法棧
    每個線程都有自己獨立的本地方法棧,用於存儲調用本地方法(native method)的信息。

下面我們來驗證一下,對象是不是存在堆區的,方法調用相關的數據是不是存在棧區的。
 
 

堆區溢出

java堆用於存儲對象實例,只要不斷的創建對象,並保證避免垃圾回收機制清除這些對象,在達到最大堆容量限制的時候就會產生內存溢出異常。-Xmx10m參數表示把堆區的最大容量變小調成10M,-XX:+HeapDumpOnOutOfMemoryError參數表示生成堆區快照文件。

vm 參數:-Xmx10m -XX:+HeapDumpOnOutOfMemoryError

import java.util.ArrayList;
import java.util.List;

/**
 * vm args: -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOOM {

    static class OOMObject {
    }

    public static void main(String[] args) {
        List<OOMObject> list  = new ArrayList<>();
        while(true) {
            list.add(new OOMObject());
        }
    }
}

 

輸出結果

第一行 java.lang.OutOfMemoryError: Java heap space 表示java堆區空間溢出,說明了對象是存儲在堆區中的。

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10876.hprof ...
Heap dump file created [13387910 bytes in 0.036 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:261)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
	at java.util.ArrayList.add(ArrayList.java:458)
	at jvm.HeapOOM.main(HeapOOM.java:17)

在項目的路徑下會生成 java_pid10876.hprof 文件,這個文件就是dump出來的堆轉儲快照文件。我們用Memory Analyzer打開看一下。

在這裏插入圖片描述

點擊上圖最下面的details鏈接可以看到更詳細的信息:

詳情截圖

可以看到OOMObject對象佔了93%的內存,罪魁禍首就是它了。

 

棧區溢出

每個方法在執行時都會創建一個棧幀,-Xss160k參數表示棧區的容量爲160k,然後遞歸調用方法,讓棧區溢出。

vm 參數: -Xss160k

/**
 * vm args: -Xss160k
 */
public class JavaVMStackSOF {

    static int stackLength = 1;

    /**
     * 遞歸調用,使其超出棧的最大深度
     */
    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) {

        JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
        try{
            javaVMStackSOF.stackLeak();
        }catch (Throwable e) {
            System.out.println("stack length: " + stackLength);
            throw e;
        }
    }
}

 

輸出結果

Exception in thread “main” java.lang.StackOverflowError 表示java棧區空間溢出,說明了方法調用是存儲在棧區中的。

stack length: 772
Exception in thread "main" java.lang.StackOverflowError
	at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
	at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
	at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
	at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
	at JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:13)
	......

 
瞭解了一些JVM的知識對我們平時編程還是很有用的哦,比如看到java.lang.OutOfMemoryError: Java heap space 異常就知道是堆區溢出了,可能是對象創建太多了;看到java.lang.StackOverflowError異常就知道是棧區溢出了,馬上去查查是不是方法調用有問題。

這樣定位問題是不是更精準了呢?

 
 
 
 
 
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章