JVM真香系列:輕鬆理解class文件到虛擬機(上)

關注「Java後端技術全棧」html

回覆「000」獲取大量電子書java

認識JVM

什麼是JVM

JVM 全稱 Java Virtual Machine,也就是咱們耳熟能詳的 Java 虛擬機。它能識別 .class後綴的文件,而且可以解析它的指令,最終調用操做系統上的函數,完成咱們想要的操做。後端

可能有部分小夥伴學習過C++,C++開發出來的程序,編譯成二進制文件後,就能夠直接執行了,操做系統是可以識別的。安全

可是我們開的的Java程序就不同了,使用javac命令編譯出來的的.class文件以後,操做系統是不能識別的,須要對應JVM去作一個轉換後,操做系統才能識別。數據結構

咱們爲何不能像 C++ 同樣,直接在操做系統上運行編譯後的二進制文件呢?而非要搞一個處於程序與操做系統中間層的虛擬機呢?oracle

這就是 JVM的過人之處了。你們都知道,Java 是一門抽象程度特別高的語言,提供了自動內存管理等一系列的特性。這些特性直接在操做系統上實現是不太可能的,因此就須要JVM 進行作一系列的轉換。jvm

你們一開始學Java的時候,就知道有個Write Once, Run Everywhere。就是咱們編寫了一個java文件通過編譯成.class文件後,能夠在各類系統中進行運行。函數

其實這裏是有個前提的,咱們須要在對應操做系統中安裝對應的JVM,而後咱們的.class文件就能運行了。工具

好比:Windows操做系統有對應的JDK安裝版本、Linux也有對應的JDK安裝版本等。學習

認識JDK

Java Development Kit (JDK) 是Sun公司(已被Oracle收購)針對Java開發員的軟件開發工具包。自從Java推出以來,JDK已經成爲使用最普遍的Java SDK(Software development kit)。

經非官方調查,目前JDK8是使用者最多的版本。

JDK14將在4月和7月收到安全更新,而後由9月到期的非LTS版本的JDK 15取代。JDK14包括16項新功能,例如JDK Flight Recorder事件流,模式匹配和開關表達式等特徵。

JDK9以後,Oracle採用了新的發佈週期:每6個月發佈一個版本,每3年發佈一個LTS版本。JDK14是繼JDK9以後發佈的第四個版本, 該版本爲非LTS版本,最新的LTS版本爲JDK11

下面是JDK版本狀況

這個混個眼熟就行,隨時關注JDK版本更新和新特性。

官網地址:https://www.oracle.com/java/

關於JDK安裝這裏就省略。

JDK、JRE、JVM的關係

上面已經說過JDKJVM的相關概念,

JRE全程Java Runtime Environment,是運行基於Java語言編寫的程序所不可缺乏的運行環境。也是經過它,Java的開發者才得以將本身開發的程序發佈到用戶手中,讓用戶使用。

三者究竟是什麼關係呢?

關於三者關係請看官網

https://docs.oracle.com/javas...

JDK中包含JRE,也包括JDK,而JRE也包括JDK。範圍關係:JDK>JRE>JVM

".java"文件到".class"文件

javac命令

編寫一個HelloWorld.java文件

內容就是一個Java入門

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

打開CMD,進入當前目錄,使用命令

javac HelloWorld.java

就編譯出HelloWorld.class

編譯過程

這個javac命令過程到底幹了些什麼呢?

javac背後大體作了這些操做

這個流程

一、詞法分析

讀取源代碼,一個字節一個字節的讀取,找出其中咱們定義好的關鍵字(如Java中的if、else、for、while等關鍵詞,識別哪些if是合法的關鍵詞,哪些不是),這就是詞法分析器進行詞法分析的過程,其結果是從源代碼中找出規範化的Token流。

二、語法分析

經過語法分析器對詞法分析後Token流進行語法分析,這一步檢查這些關鍵字組合再一次是否符合Java語言規範(如在if後面是否是緊跟着一個布爾判斷表達式),詞法分析的結果是造成一個符合Java語言規範的抽象語法樹。

三、語義分析

經過語義分析器進行語義分析。語音分析主要是將一些難懂的、複雜的語法轉化成更加簡單的語法,結果造成最簡單的語法(如將foreach轉換成for循環 ,好有註解等),最後造成一個註解事後的抽象語法樹,這個語法樹更爲接近目標語言的語法規則。

四、生成字節碼

經過字節碼生產器生成字節碼,根據通過註解的語法抽象樹生成字節碼,也就是將一個數據結構轉化爲另外一個數據結構。最後生成咱們想要的.class文件。

使用十六進制查看class文件內容

我只用的是Notepad++,選中文本→插件→Converter→ASCII->HEX

class文件的開頭就是

CAFEBABE

想要學習這裏的十六進制的字節碼的含義能夠參考

https://docs.oracle.com/javas...

-

javap查看class文件內容

javap是 Java class文件分解器,能夠反編譯(即對javac編譯的文件進行反編譯),也能夠查看java編譯器生成的字節碼。

新建一個User.java源文件,通過javac編譯後,生成User.classs

package com.tian.demo.test;
public class User {
    private int age = 22;
    private String name = "tian";
    public int addAge() {
        return age = age + 1;
    }
    public static void main(String[] args) {
    }
}

使用javap命令

javap -v User.class >log.txt

打開log.txt

Classfile /D:/workspace/new/demo/src/main/java/com/tian/demo/test/User.class
  Last modified 2020-11-5; size 441 bytes
  MD5 checksum 2fa72d3f53bd9f138e0bfae82aba67e3
  Compiled from "User.java"
public class com.tian.demo.test.User
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #5.#22         // com/tian/demo/test/User.age:I
   #3 = String             #23            // tian
   #4 = Fieldref           #5.#24         // com/tian/demo/test/User.name:Ljava/lang/String;
   #5 = Class              #25            // com/tian/demo/test/User
   #6 = Class              #26            // java/lang/Object
   #7 = Utf8               age
   #8 = Utf8               I
   #9 = Utf8               name
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               addAge
  #16 = Utf8               ()I
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               SourceFile
  #20 = Utf8               User.java
  #21 = NameAndType        #11:#12        // "<init>":()V
  #22 = NameAndType        #7:#8          // age:I
  #23 = Utf8               tian
  #24 = NameAndType        #9:#10         // name:Ljava/lang/String;
  #25 = Utf8               com/tian/demo/test/User
  #26 = Utf8               java/lang/Object
{
  public com.tian.demo.test.User();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        22
         7: putfield      #2                  // Field age:I
        10: aload_0
        11: ldc           #3                  // String tian
        13: putfield      #4                  // Field name:Ljava/lang/String;
        16: return
      LineNumberTable:
        line 3: 0
        line 4: 4
        line 5: 10
  public int addAge();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: aload_0
         2: getfield      #2                  // Field age:I
         5: iconst_1
         6: iadd
         7: dup_x1
         8: putfield      #2                  // Field age:I
        11: ireturn
      LineNumberTable:
        line 8: 0
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 13: 0
}
SourceFile: "User.java"
魔數與class文件版本
常量池
訪問標誌
類索引、父類索引、接口索引
字段表集合
方法表集合
屬性表集合

而後JVM就能夠讀取這個User.class文件進行解析等一系列的操做。

以上就是咱們的Java文件到class文件。

後續還會更新一系列JVM相關文章,敬請期待~

推薦閱讀:

《Java Web企業項目實戰》.pdf

《MySQL開發者SQL權威指南》.pdf

關注公衆號「Java後端技術全棧」

免費獲取500G最新學習資料

相關文章
相關標籤/搜索