1. 程式人生 > 實用技巧 >java學習-jvm-記憶體分配

java學習-jvm-記憶體分配

  • 參考文章
  • java記憶體結構
    • 堆(heap)
      • 記憶體中共享的執行時資料區域,用於儲存例項化的java物件,當jvm啟動時,該區域會被例項化
      • 在當前區域中只會存在java class物件以及陣列資料,對於堆的空間大小可以通過啟動引數 設定實現固定或動態
      • Jvm提供初始化和修改堆空間大小的使用者控制;當 new 關鍵字在使用時,例項化的物件會被存放在堆空間中,但對於當前的物件的引用資料,會被儲存在棧中
      • 每個jvm有且僅有一個heap區域
      • 對於 heap區域gc是強制的
      • Heap實際是由 eden + survivor(0/1) + oldGeneration
    • 方法區(method area):實際也是屬於heap的
      • 屬於heap中的邏輯區域,在jvm啟動時建立
      • 為class 結構/方法資料/構造器欄位資料 分配的記憶體區域,空間大小也可以是固定或動態的;
      • 可以固定大小或動態擴容, 無需是連續的記憶體空間
      • 雖然 method area 屬於 heap,但對於當前區域gc並非是強制的
      • 對於 run-time constant pool 是屬於 method-area:主要是儲存 編譯時常量資料(字串和數字)以及執行時的方法以及欄位引用
    • 棧(stack)
      • 線上程建立時,棧也會隨之建立,棧是屬於執行緒的私有區域;當方法存在返回值和動態連結庫,被用來儲存資料和部分結果
      • 棧空間可以是固定的或動態的,也可以通過java程式碼在建立執行緒時來指定棧空間大小
      • 棧的記憶體空間無需是連續記憶體
      • 程式語言中的棧定義
        • 對於呼叫棧也可以稱為棧,呼叫者會將返回的地址壓入棧中然後呼叫下一個執行,當他完成時,呼叫棧的結果有可能出棧也有可能入棧以及轉移指定地址的控制權;如果呼叫其他的行為時,則會將返回的地址寫入呼叫的棧中;對於程式的執行就是出棧和入棧的結果
        • 對於高階語言中,特殊點在於對於呼叫棧的操作是隱藏的,只會提供相關的操作方法,而不允許直接操作記憶體
      • java 中的棧
        • 每個jvm執行緒都會擁有一個私有的棧空間,在建立執行緒時同步建立當前執行緒的棧空間;
        • jvm棧中儲存的是幀(資料控制代碼)
        • jvm棧和c的棧一致,都是儲存方法區域性變數和執行時結果;
        • 由於jvm中棧空間絕對不會被直接操作;堆分配棧幀
        • jvm棧的空間不需要是連續記憶體
    • 本地方法棧(native method stack)
      • 也可以成為C語言棧,native method並不是由java程式碼生成,其記憶體只和執行緒關聯,線上程建立時會分配記憶體, 也可以固定大小或動態
    • program counter registers
      • 每個執行特殊方法任務的jvm執行緒都會存在一個 程式計數器暫存器相關聯;對於存在程式計數器的非native方法會儲存有效的JVM指令地址,而 對於native方法中,該程式計數器的值時未定義的;
      • 在一些特殊的平臺中,程式計數器暫存器支援儲存返回資料的記憶體地址或一個 native 指標
    • 非堆(non-heap)
      • Metaspace : 元資料 ,一般包含一些class 資料
      • code cache:
        • 對於code cache首先需要了解 java編譯過程,以及jit
        • 對於一個java程式碼的執行過程; 首先 前端編譯(javac): .java -> .class ;對於.class的執行並不能直接被cpu識別;
        • 此時就引出了後端編譯器(執行 class 資料)
        • 對於編譯後的機器碼資料,此時就會儲存在codecache區域; 但在該區域不止包含jit編譯後的機器碼,也包含 jni (native)程式碼
  • 原生型別資料儲存
    • 對於 編譯時初始化的原生資料(int等),會在編譯時存放到run-time constant pool中;
    • 對於方法引數中的原生資料是該方法執行時壓入棧中的;
    • 對於類的屬性,會在當前類例項化時直接存放在當前物件例項所在的堆區域

      

public class JVMPrimaryDataDemo {
    // 對於當前常量資料,在類載入期間時將class檔案轉為Class物件後,初始化時會將當前資料存放到run-time constant pool 區域
    public static final int CONSTANT_COMPILER_DATA = 1;
    public int j = 4; // 對於該欄位的資料是存放在構造方法中的;是存在於例項化物件中(堆)

    public static void main(String[] args) {
        // 而對於方法中的資料在執行時會被直接載入到棧中
        int i = 3; // 該數字在編譯後會被翻譯為 iconst_數值
    }
    /**
     * 對 編譯後的class檔案 使用javap -v 解析
     * // 常量池 == run-time constants pool
     * Constant pool:
     *    #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
     *    #2 = Class              #4             // java/lang/Object
     *    #3 = NameAndType        #5:#6          // "<init>":()V
     *    #4 = Utf8               java/lang/Object
     *    #5 = Utf8               <init>
     *    #6 = Utf8               ()V
     *    #7 = Class              #8             // com/xing/level/JVMPrimaryDataDemo
     *    #8 = Utf8               com/xing/level/JVMPrimaryDataDemo
     *    #9 = Utf8               CONSTANT_COMPILER_DATA // 欄位名
     *   #10 = Utf8               I
     *   #11 = Utf8               ConstantValue
     *   #12 = Integer            1 // 宣告的常量資料
     *   #13 = Utf8               Code
     *   #14 = Utf8               LineNumberTable
     *   #15 = Utf8               LocalVariableTable
     *   #16 = Utf8               this // 當前物件引用
     *   #17 = Utf8               Lcom/xing/level/JVMPrimaryDataDemo;
     *   #18 = Utf8               main // 方法名
     *   #19 = Utf8               ([Ljava/lang/String;)V
     *   #20 = Utf8               args // 引數名
     *   #21 = Utf8               [Ljava/lang/String;
     *   #22 = Utf8               i // 區域性變數名
     *   #23 = Utf8               SourceFile
     *   #24 = Utf8               JVMPrimaryDataDemo.java
     */
}