JVM的基礎學習到紮實

                      JVM的基礎學習到紮實--class文件詳解

Java工作幾年之後,特別是從基礎到工作一年以上,還有的是培訓班出來的,自學的,寫了大量的代碼,但是,這些代碼是怎麼工作的,怎麼執行的,怎麼變成我們需要的結果?對於沒有接觸過JVM的同學來說是一個問題。也有的不知道JVM是幹什麼用的,怎麼來的。這是問題根本。

作爲Java開發者,我們都知道,自己在eclipse寫的代碼,執行後就看到結果,但是執行過程中是怎麼樣的,需要我們去搞懂。

1.JVM幹什麼用的?

JVM就是虛擬機,專門是用來解析寫Java的字節碼,最後變成我們自己想要的結果。如下圖所示:

 2.jdk,jre,jvm三者之間的關係

JDK有兩種,分別爲OracleJDK和OpenJDK

查看JDK的版本:

進去終端裏面輸入命令查看:java -version;

java -version;

顯示結果:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
192:~ mac1094$ 

以上就是OracleJDK。

如果是OpenJDK,顯示信息:

openjdk version "1.8.0_144"
OpenJDK Runtime Environment (build 1.8.0_144-b01)
OpenJDK 64-Bit Server VM (build 25.144-b01, mixed mode)

JVM的運行模式有兩種:Server模式和Client模式

兩種模式的區別在於:

Client模式啓動速度較快,Server模式啓動較慢;

但是啓動進入穩定期長期運行之後Server模式的程序運行速度比Client要快很多。

因爲Server模式啓動的JVM採用的是重量級的虛擬機,對程序採用了更多的優化;而Client模式啓 動的JVM採用的是輕量級的虛擬機。所以Server啓動慢,但穩定後速度比Client遠遠要快。

3.JVM的架構圖(很重要)

 

根據架構圖中可以看出,JVM架構由三大板塊構成,首先要記住三大板塊的邏輯結構關係,然後再記住三大板塊裏的功能,切記死記硬背,理解的基礎上記憶,每天畫一下架構圖,堅持一週就可以記得牢固。

JVM的執行流程:

4.class文件是怎麼加載的?(重在理解)

我們隨便寫一個Java源代碼,打開他的Class文件(使用Hex Editor等其他工具打開),如下圖所示:

class文件的結構很複雜,需要理解:如下圖:

魔數:class文件最初的4個字節都是以"0xCAFEBABE"來的,判斷的唯一標準。

版本號:分爲主版本號和副版本號,各佔2字節。

minor_version( JDK次版本號),
major_version( JDK主版本號)

可以查看JVM編譯的代碼,使用命令工具:javap -v class文件名

192:myapp mac1094$ javap -v Math.class

顯示結果:

Classfile /Users/mac1094/Desktop/myapp/Math.class
  Last modified May 17, 2019; size 401 bytes
  MD5 checksum 1b66de1534877806cd9a6747dbfc330a
  Compiled from "Math.java"
public class Math
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#14         // java/lang/Object."<init>":()V
   #2 = Fieldref           #15.#16        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #17.#18        // java/io/PrintStream.println:(I)V
   #4 = Class              #19            // Math
   #5 = Class              #20            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               SourceFile
  #13 = Utf8               Math.java
  #14 = NameAndType        #6:#7          // "<init>":()V
  #15 = Class              #21            // java/lang/System
  #16 = NameAndType        #22:#23        // out:Ljava/io/PrintStream;
  #17 = Class              #24            // java/io/PrintStream
  #18 = NameAndType        #25:#26        // println:(I)V
  #19 = Utf8               Math
  #20 = Utf8               java/lang/Object
  #21 = Utf8               java/lang/System
  #22 = Utf8               out
  #23 = Utf8               Ljava/io/PrintStream;
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (I)V
{
  public Math();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: bipush        10
         9: imul
        10: istore_3
        11: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        14: iload_3
        15: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        18: return
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 4
        line 6: 11
        line 7: 18
}
SourceFile: "Math.java"

常量池計數器:

常量池是class文件中非常重要的結構,它描述着整個class文件的字面量信息。常量池是由一組 constant_pool結構體數組組成的,而數組的大小則由常量池計數器指定。常量池計數器 constant_pool_count 的值 =constant_pool表中的成員數+ 1。constant_pool表的索引值只有在大於 0 且小於constant_pool_count時纔會被認爲是有效的。

常量池數據區:

字面量和符號引用

訪問標誌:

訪問標誌,access_flags 是一種掩碼標誌,用於表示某個類或者接口的訪問權限及基礎屬性。

類索引:

類索引,this_class的值必須是對constant_pool表中項目的一個有效索引值。constant_pool表 在這個索引處的項必須爲CONSTANT_Class_info 類型常量,表示這個 Class 文件所定義的類或接 口。

父類索引:

父類索引,對於類來說,super_class 的值必須爲 0 或者是對constant_pool 表中項目的一個有 效索引值。

如果它的值不爲 0,那 constant_pool 表在這個索引處的項必須爲CONSTANT_Class_info 類型常 量,表示這個 Class 文件所定義的類的直接父類。當前類的直接父類,以及它所有間接父類的 access_flag 中都不能帶有ACC_FINAL 標記。對於接口來說,它的Class文件的super_class項的 值必須是對constant_pool表中項目的一個有效索引值。constant_pool表在這個索引處的項必須爲 代表 java.lang.Object 的 CONSTANT_Class_info 類型常量 。

如果 Class 文件的 super_class的值爲 0,那這個Class文件只可能是定義的是 java.lang.Object類,只有它是唯一沒有父類的類。

接口計數器:

 接口計數器,interfaces_count的值表示當前類或接口的【直接父接口數量】。

接口信息數據區:

接口表,interfaces[]數組中的每個成員的值必須是一個對constant_pool表中項目的一個有效索引 值, 它的長度爲 interfaces_count。每個成員interfaces[i] 必須爲 CONSTANT_Class_info類型常量,其中 【0 ≤ i <interfaces_count】。在interfaces[]數組 中,成員所表示的接口順序和對應的源代碼中給定的接口順序(從左至右)一樣,即interfaces[0]對 應的是源代碼中最左邊的接口。

字段計數器:

字段計數器,fields_count的值表示當前 Class 文件 fields[]數組的成員個數。 fields[]數組 中每一項都是一個field_info結構的數據項,它用於表示該類或接口聲明的【類字段】或者【實例字 段】。

字段信息數據區:

字段表,fields[]數組中的每個成員都必須是一個fields_info結構的數據項,用於表示當前類或接 口中某個字段的完整描述。 fields[]數組描述當前類或接口聲明的所有字段,但不包括從父類或父接 口繼承的部分。

方法計數器:

方法計數器, methods_count的值表示當前Class 文件 methods[]數組的成員個數。Methods[] 數組中每一項都是一個 method_info 結構的數據項。

方法信息數據區:

方法表,methods[] 數組中的每個成員都必須是一個 method_info 結構的數據項,用於表示當前類 或接口中某個方法的完整描述。

如果某個method_info 結構的access_flags 項既沒有設置 ACC_NATIVE 標誌也沒有設置 ACC_ABSTRACT 標誌,那麼它所對應的方法體就應當可以被 Java 虛擬機直接從當前類加載,而不需 要引用其它類。

method_info結構可以表示類和接口中定義的所有方法,包括【實例方法】、【類方法】、【實例初始 化方法】和【類或接口初始化方法】。

methods[]數組只描述【當前類或接口中聲明的方法】,【不包括從父類或父接口繼承的方法】。

屬性計數器:

屬性計數器,attributes_count的值表示當前 Class 文件attributes表的成員個數。 attributes表中每一項都是一個attribute_info 結構的數據項。

屬性信息數據區:

屬性表,attributes 表的每個項的值必須是attribute_info結構。

在Java 7 規範裏,Class文件結構中的attributes表的項包括下列定義的屬性: InnerClasses
、 EnclosingMethod 、 Synthetic 、Signature、SourceFile,SourceDebugExtension 、Deprecated、RuntimeVisibleAnnotations 、RuntimeInvisibleAnnotations以及 BootstrapMethods屬性。

對於支持 Class 文件格式版本號爲 49.0 或更高的 Java 虛擬機實現,必須正確識別並讀取 attributes表中的Signature、RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations屬性。對於支持Class文件格式版本號爲 51.0 或更高的 Java 虛擬機實現,必須正確識別並讀取 attributes表中的BootstrapMethods屬性。Java 7 規範 要求 任一 Java 虛擬機實現可以自動忽略 Class 文件的 attributes表中的若干 (甚至全部) 它不可 識別的屬性項。任何本規範未定義的屬性不能影響Class文件的語義,只能提供附加的描述信息 。

 

5.以上就是JVM的基礎的class文件的來源,很重要,需要記住。爲後面的特徵打好基礎。後續會有類加載機制和運行數據區,垃圾回收機制,如有錯誤,可以留言更正!謝謝!

 

 

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