【JVM】運行時數據區

運行時數據區

圖示:
運行時數據區

Method Area(方法區)

方法區是各個線程內存共享的區域,在虛擬機啓動時創建;用於存儲已被虛擬機加載的類信息、常量、靜態變量、即使編譯器編譯後的代碼等數據

雖然Java虛擬機規範把方法區描述爲堆的一個邏輯分佈,但是方法區通常被稱爲Non-Heap(非堆),目的是與Java堆區分開;當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常

The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is created on virtual machine start-up. Although the method area is logically part of the heap,… If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an OutOfMemoryError.

注意

  1. 方法區在JDK 8 中就是Metaspace,在JDK6或7中被稱爲Perm Space
  2. Run-Time Constant Pool
    Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,用於存存放編譯時期生成的各種字面量和符號引用,這部分內容將在類加載進入方法區的運行時常量池存放;

Each run-time constant pool is allocated from the Java Virtual Machine’s method area


Heap(堆)

Java堆是Java虛擬所管理內存中最大的一塊,在虛擬器啓動時創建,被所有線程所共享;對象實例以及數組都在堆上分配內存空間;垃圾回收的主要區域

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated. The heap is created on virtual machine start-up.

Java Virtual Machine Stacks(虛擬機棧)

虛擬機棧是一個線程執行的區域,保存着一個線程中方法的調用狀態;換句話說,一個Java線程的運行狀態,由一個虛擬機棧來保存,所以虛擬機棧肯定是線程私有的,隨着線程的創建而創建;

每一個被線程執行的方法,稱爲該棧中棧幀,即每一個方法對應一個棧幀;一個方法被調用就會向虛擬機棧中壓入一個棧幀;一個方法調用完成,就會將棧幀從棧中彈出

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames

圖解

比如執行以下方法:

    public void A() {
       B();
       C();
    }

虛擬機棧存儲運行如下:
虛擬機棧

棧:先進後出
A frame is used to store data and partial results, as well as to perform dynamic linking, return values for methods, and dispatch exceptions. A new frame is created each time a method is invoked. A frame is destroyed when its method invocation completes, whether that completion is normal or abrupt (it throws an uncaught exception). … Note that a frame created by a thread is local to that thread and cannot be referenced by any other thread.

棧幀

每一個棧幀對應一個方法,壓棧意味着方法的調用,出棧意味着方法執行完成;

每個棧幀中包括局部變量表(Local Variables)、操作數棧(Operand Stack)、指向運行時常量池的引用(A reference to the run-time constant pool)、方法返回地址(Return Address)和附加信息

局部變量表

方法中定義的局部變量一個方法的參數存儲位置;局部變量表中的變量不可以直接使用,如果需要使用的話,必須通過相關的指令將其加載到操作數中使用

操作數棧

以壓棧和出棧的方式存儲操作數

動態鏈接

每個操作數棧都包含一個指向運行時常量池中

方法返回地址

當一個方法執行後,只有兩種方式可以退出,一種是遇到方法的返回地址;一種是遇見異常,並且這個異常沒有在方法體內得到處理

The PC Register(程序計數器)

首先,我們來看一個問題,我們都知道以後JVM進程中有多個線程在執行,而線程中的內容是否能夠執行,是根據CPU調度的;假如線程A正在執行到某個地方,突然失去了CPU的執行權。切換到了線程B,讓後當線程A再次獲得CPU執行權的時候,怎麼能夠繼續執行那?那麼繼續要維護一下變量,用於記錄線程執行的到哪一行的位置;程序計數器隨之誕生。

程序計數器佔用的內存空間很小,由於Java虛擬機的多線程是通過線程輪流切換,並分配處理器執行時間的方式來實現的;在任意時刻,一個處理器只會執行一條線程的指令,因此,爲了線程切換後能夠恢復到正確的執行位置,每條線程需要有一個獨立的程序計數器(線程私有)

如果線程正在執行Java方法,則計數器記錄的是正在執行的虛擬機字節碼指令的地址;

如果正在執行的是Native方法,則這個計數器爲空。

The Java Virtual Machine can support many threads of execution at once (JLS §17). Each Java Virtual Machine thread has its own pc (program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method (§2.6) for that thread. If that method is not native, the pc register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread is native, the value of the Java Virtual Machine’s pc register is undefined. The Java Virtual Machine’s pc register is wide enough to hold a returnAddress or a native pointer on the specific platform.

Native Method Stacks(本地方法棧)

如果當前線程執的方法時Native類型時,這些方法就會在本地方法中執行

結合字節碼指令理解虛擬機棧
public class Person {
        private String name = "demo";
        private int age;
        private final double salary = 1000;
        private static String address;
        private final static String hobby = "Programming";
        private Object obj = new Object();
    
        public void say() {
            System.out.println("person say...");
        }
        
        public static int calc(int op1, int op2) {
            op1 = 3;
            int result = op1 + op2;
            return result;
        }
    
        public static void main(String[] args) {
            System.out.println(calc(1, 2));
        }
    }

    Compiled from "Person.java"
    public class Person {
      public Person();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: aload_0
           5: ldc           #2                  // String demo
           7: putfield      #3                  // Field name:Ljava/lang/String;
          10: aload_0
          11: ldc2_w        #4                  // double 1000.0d
          14: putfield      #6                  // Field salary:D
          17: aload_0
          18: new           #7                  // class java/lang/Object
          21: dup
          22: invokespecial #1                  // Method java/lang/Object."<init>":()V
          25: putfield      #8                  // Field obj:Ljava/lang/Object;
          28: return
    
      public void say();
        Code:
           0: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc           #10                 // String person say...
           5: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
    
      public static int calc(int, int);
        Code:
           0: iconst_3           	//將int類型常量2壓入操作數棧
           1: istore_0			 	//將int類型值存入局部變量0	
           2: iload_0            	//從局部變量0中裝載int類型的值
           3: iload_1            	//從局部變量1中裝載int類型的值
           4: iadd              	// 執行int類型的加法
           5: istore_2          	//將int類型值存入局部變量表中
           6: iload_2            	//從局部變量2中裝載int類型的值
           7: ireturn            	//從方法中返回int類型的值
    
      public static void main(java.lang.String[]);
        Code:
           0: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: iconst_1
           4: iconst_2
           5: invokestatic  #12                 // Method calc:(II)I
           8: invokevirtual #13                 // Method java/io/PrintStream.println:(I)V
          11: return
    }

主內存、工作內存與Java內存區域中的Java堆、棧、方法區等並不是同一層次的內存劃分。兩者基本上是沒有關係的,如果兩者一定要勉強對應起來的,從變量、主內存、工作內存的定義看,主內存主要對應於Java堆中的對象實例數據部分,而工作內存則對應了虛擬機棧中部分區域

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