JAVA程序運(yùn)行原理分析

JAVA程序的運(yùn)行原理:

1. 源文件(.java源代碼)通過編譯器編譯成字節(jié)碼文件class。

2. 通過JVM中的解釋器將字節(jié)碼文件生成對(duì)應(yīng)的可執(zhí)行文件,運(yùn)行。

  1. 將編譯后的程序加載到方法區(qū),存儲(chǔ)類信息。

4. 運(yùn)行時(shí),JVM創(chuàng)建線程來執(zhí)行代碼,在虛擬機(jī)棧和程序計(jì)數(shù)器分配獨(dú)占的空間。根據(jù)方法區(qū)里的指令碼,在虛擬機(jī)棧對(duì)線程進(jìn)行操作,程序計(jì)數(shù)器保存線程代碼執(zhí)行到哪個(gè)位置。

“一處編寫,處處運(yùn)行”:編譯后,不依賴于平臺(tái)環(huán)境,在各種操作系統(tǒng)均可運(yùn)行。

image

Class字節(jié)碼文件:是一個(gè)二進(jìn)制文件,包含了JAVA程序執(zhí)行的字節(jié)碼,包含的信息有版本、訪問標(biāo)志、常量池、當(dāng)前類、超級(jí)類、接口、字段、方法、屬性等,中間沒有任何分隔符,文件開頭有一個(gè)特殊標(biāo)志,用16進(jìn)制表示為0xcafebabe。

Class文件反編譯后,可通過“JVM指令碼表”查看指令,這些指令運(yùn)行時(shí)都保存在方法區(qū)。

從指令碼中看出,沒有定義構(gòu)造函數(shù)時(shí),會(huì)有隱式的無參構(gòu)造函數(shù);方法參數(shù)保存在虛擬機(jī)棧里的本地變量表,每一個(gè)main方法都有一個(gè)String數(shù)組參數(shù),保存在本地變量表的變量0。


JVM運(yùn)行時(shí)數(shù)據(jù)區(qū):包含線程共享部分和線程獨(dú)占部分。

線程共享:線程共同訪問的內(nèi)存數(shù)據(jù)空間,隨著JVM(虛擬機(jī))或者GC(垃圾回收)而創(chuàng)建和銷毀。包含方法區(qū)和堆內(nèi)存。

1、方法區(qū):JVM用來存儲(chǔ)加載的類信息、常量、靜態(tài)變量、編譯后的代碼等數(shù)據(jù)。在JVM規(guī)范中,這是一個(gè)邏輯區(qū),根據(jù)不同的虛擬機(jī)有不同的具體實(shí)現(xiàn),如oracle的HotSpot的方法區(qū),在java7中放在永久代,java8中放在元數(shù)據(jù)空間,并通過GC機(jī)制對(duì)這個(gè)區(qū)域進(jìn)行管理。目前有三大Java虛擬機(jī):HotSpot,oracle JRockit,IBM J9。

2、堆內(nèi)存:存放對(duì)象的實(shí)例,在JVM啟動(dòng)時(shí)創(chuàng)建??杉?xì)分為老年代、新生代,垃圾回收器主要就是管理堆內(nèi)存,如果滿了就會(huì)出現(xiàn)OOM(OutOfMemoryError)。

線程獨(dú)占:每個(gè)線程都會(huì)有自己獨(dú)立的空間,隨著線程的生命周期而創(chuàng)建和銷毀。包含虛擬機(jī)棧、本地方法棧、程序計(jì)數(shù)器。

1、虛擬機(jī)棧:即虛擬機(jī)執(zhí)行JAVA代碼的棧,每個(gè)線程都會(huì)在這有一個(gè)私有空間。線程棧由多個(gè)棧幀(Stack Frame)組成,一個(gè)線程會(huì)執(zhí)行一個(gè)或多個(gè)方法,一個(gè)方法對(duì)應(yīng)一個(gè)棧幀。棧幀內(nèi)容包括:局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法返回地址、附加信息等。棧內(nèi)存默認(rèn)最大是1M,超出則拋StackOverflowError。

2、本地方法棧:即虛擬機(jī)執(zhí)行Native本地方法的棧,和虛擬機(jī)棧的主要區(qū)別是執(zhí)行的方法不同。在虛擬機(jī)規(guī)范沒有規(guī)定具體的實(shí)現(xiàn),由不同的虛擬機(jī)廠商去實(shí)現(xiàn)。HotSpot虛擬機(jī)中虛擬機(jī)棧和本地方法棧的實(shí)現(xiàn)方式是一樣的,超出大小后也會(huì)拋出StackOverflowError。

3、程序計(jì)數(shù)器:記錄當(dāng)前線程執(zhí)行字節(jié)碼的位置,存儲(chǔ)的是字節(jié)碼指定地址,如果執(zhí)行Native方法,則計(jì)數(shù)器值為空。每個(gè)線程在這都有一個(gè)私有空間,占用很少的內(nèi)存空間。

CPU同一時(shí)間只會(huì)執(zhí)行一條線程中的指令,JVM多線程會(huì)輪流切換并分配CPU執(zhí)行時(shí)間,在線程切換后,需要通過程序計(jì)數(shù)器來回復(fù)正確的執(zhí)行位置。

查看class文件內(nèi)容

使用Demo.Java進(jìn)行測(cè)試, 運(yùn)行javac Demo.java編譯成class文件, 然后運(yùn)行javap -v Demo.class > Demo.txt查看class文件內(nèi)容

Demo.Java

public class Demo{
    public static void main(String[] args){
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

Demo.txt

Classfile /E:/*/Demo.class
  Last modified 2019-6-30; size 412 bytes
  MD5 checksum efd785af33e58aa9fc9834110b74b87b
  Compiled from "Demo.java"
public class Demo
  minor version: 0      //次版本號(hào)
  major version: 52       //主版本號(hào) 版本號(hào)規(guī)則: JDK5,6,7,8分別對(duì)應(yīng)49,50,51,52
  flags: ACC_PUBLIC, ACC_SUPER    //訪問標(biāo)志
Constant pool:    // 常量池 類信息包含的靜態(tài)常量, 編譯之后就能確認(rèn)
   #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            // Demo
   #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               Demo.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               Demo
  #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 Demo();     // 默認(rèn)隱式無參的構(gòu)造函數(shù)
    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[]);    //程序的入口main方法
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC     //訪問控制
    Code:
      stack=3, locals=5, args_size=1      //方法棧棧幀中操作數(shù)棧的深度,本地變量數(shù)量,參數(shù)數(shù)量
         0: sipush        500     //Jvm執(zhí)行引擎執(zhí)行這些源碼編譯過后的指令碼, javap翻譯出來的
         3: istore_1            //是操作符, class 文件內(nèi)存儲(chǔ)的是指令碼, 前面的數(shù)據(jù)是偏移量,
         4: bipush        100     //Jvm根據(jù)這個(gè)去區(qū)分不同的指令. 詳情參照'JVM指令碼表'
         6: istore_2
         7: iload_1
         8: iload_2
         9: idiv
        10: istore_3
        11: bipush        50
        13: istore        4
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: iload_3
        19: iload         4
        21: iadd
        22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        25: return
      LineNumberTable:
        line 3: 0
        line 4: 4
        line 5: 7
        line 6: 11
        line 7: 15
        line 8: 25
}
SourceFile: "Demo.java"

程序完整運(yùn)行分析

img
img
image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容