JVM數據區解析

這篇文章以java爲例講解java源碼到字節碼到字節碼對象再到jvm的內存中整個過程爲例。從而來理解JVM的原理。JVM由ClassLoader,JVM運行時數據區,執行引擎三部分構成。這篇文章主要會講解JVM運行時數據區的各個分區的內在特點以及各個分區之間的關係。

一,JVM簡介

JVM(Java Virtual Machine)也就是java虛擬機。用來運行字節碼碼的。可以運行Java,kotlin,Scala,Clojure,Groovy,Jython,JRuby,Ceylon,Eta,Haxe 10餘種語言編譯的字節碼。

後面就以Java來講解。先給上一張圖。可以看出JVM是在這個JDK中最底層的一小部分。但是他卻起到到來了極其重要的作用。

java源碼通過javac編譯成.class的字節碼文件然後在由java虛擬機運行。java具有垮平臺的特點。它跨平臺就是通過不同的操作系統上裝上不同的JDK從而擁有不同的JVM來實現的跨平臺。也就是一份編譯出來的字節碼文件可以在不同的操作系統的JVM上運行。

JVM由ClassLoader,JVM運行時數據區,執行引擎三部分構成。

下面是官方圖:

下面來一箇中文的簡圖,文章主要講解運行時數據區的內存空間的分區,每個分區的內在特點,以及分區間的相互關係。

 

 

 

二,方法區

說到方法區就要提到 元空間,永久帶 。方法區是規範,元空間,永久帶是具體實現。

  • 永久帶是jdk8之前方法區的具體實現。他是放在堆裏面的,所有他也會出現oom(out of memory),也會觸發GC。
  • 元空間是jdk8以後方法區的具體實現。他是放在直接內存中的。也就是操作系統內存。所有會出現native memory

   1,元空間的默認大小是20.75M。最大內存就是系統內存。(目前系統64位,其中16位爲保留位,最大內存就爲2的48次方=256T)

   2,元空間調優

         (1)元空間最大最小設置成一樣。這樣可以防止內存抖動。

         (2)元空間設置爲多物理內存的1/32。具體多少需要進行調試。

         (3)設置元空間的時候比起實際使用大小預留20%到30%這樣比較安全。

 方法區裏面存放了:常量,靜態變量,類信息。              

三,程序計數器

 程序計數器是指向當前線程執行的字節碼指令的(地址)行號。

 在cmd窗口執行命令   javap  -c  Test.class >Test.txt

打開Test.txt文件。程序計數器就是存放這個行號的。

Compiled from "Test.java"
public class AAA.Test {
  public AAA.Test();
    Code:
       0: aload_0
       1: invokespecial #12                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #3                  // class java/lang/Object
       8: dup
       9: invokespecial #12                 // Method java/lang/Object."<init>":()V
      12: putfield      #14                 // Field object:Ljava/lang/Object;
      15: return

  public int add();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        100
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class AAA/Test
       3: dup
       4: invokespecial #28                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #29                 // Method add:()I
      12: istore_2
      13: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_2
      17: invokevirtual #37                 // Method java/io/PrintStream.println:(I)V
      20: return
}

 

四,虛擬機棧(線程棧)

虛擬機棧裏面存放的是棧幀,一個方法對應一個棧幀。

棧幀中存放了:局部變量表,操作數棧,動態鏈接,方法出口。

  • 局部變量表:就是這個方法類的局部變量彙總。
  • 操作數棧:這些局部變量在賦值給變量引用之前要先壓入操作數棧。以及進行計算的時候臨時存放變量的棧。
  • 動態鏈接:就是存放這個方法在方法區內的內存地址。
  • 方法出口:就是返回現場。比如在執行到main方法的第9行指令開始調用add()方法,這個時候就會把這個行號存到方法出口中,當add()方法執行結束然後就回到main()方法中執行第10行指令。

一個方法執行完JVM需要做的事情。

  • 1,恢復局部變量表指針
  • 2,恢復操作數棧指針
  • 3,恢復程序技術器。
  • 4,如果方法有返回地址,需要返回。
  • 5,清理棧幀。(程序計數器去完成的)

這個是上面圖對應的java代碼

public class Test {
	public int add() {
		int a = 1;//局部變量
		int b = 2;//局部變量
		int c = (a+b)*100;
		return c;
	}
	
	public static void main(String[] args) {
		Test text=new Test();
		int result = text.add();
		System.out.println(result);
	}
}

 

五,堆

1,堆是用來存放對象的地方

2,堆的默認大小

  •       最小是是物理內存的1/64
  •       最大是1/4

3,堆內存分區

  •      yang(年輕代)和old(老年代)兩部分。默認比例爲1:2
  •      yang(年輕代)又分爲Eden(伊甸園區)和survivor0區,survivor1區。默認比例爲8:1:1

 

六,本地方法棧

   本地方法棧(Native Method Stacks)與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。虛擬機規範中對本地方法棧中的方法使用的語言、使用方式與數據結構並沒有強制規定,因此具體的虛擬機可以自由實現它。甚至有的虛擬機(譬如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。與虛擬機棧一樣,本地方法棧區域也會拋出StackOverflowError和OutOfMemoryError異常。

 

七,個內存區之間的關係

   虛擬機棧-->方法區   動態鏈接。(虛擬機棧中的棧幀對應的方法在方法區的內存地址)

   堆-->方法區   關聯是class Pointer(也就是對象執行所對應的類的Class對象所屬地址)

   方法區-->堆   類裏面的靜態對象引用。(例如類裏面的public static Test test = new Test();)

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