java Hello world 源碼執(zhí)行流程詳解

歡迎關(guān)注公眾號“Tim在路上”
hello world 作為我們學(xué)習(xí)的第一個個程序,看起來很簡單,但是要理解其執(zhí)行的具體流程還是需要很深的功底,包括對組成原理,操作系統(tǒng)的理解,今天將java hello world 進(jìn)行整理一下吧??!

廢話不多說,先上hello world

public class Main {

    public static void main(String[] args) {
        String s = "helloWorld";
        String s2 = new String("helloWorld");
        System.out.println(s);
        System.out.println(s2);
    }
}

很簡單的程序,具體執(zhí)行是怎么樣的?

相信大家都知道 java 代碼的可移植性,是由于java解釋器和虛擬機,所以處理java原代碼的過程,就是java代碼執(zhí)行的過程:

Java 代碼的運行過程?

Java 源代碼 -> 編輯器 -> 字節(jié)碼文件
字節(jié)碼 -> JVM -> 機器碼文件

每一種平臺的解釋器是不同的,但是實現(xiàn)的虛擬機是相同的,這也就是 Java 為什么能夠 跨平臺的原因

java 原文件通過編譯器編譯成.class字節(jié)碼文件,字節(jié)碼文件通過 JVM 虛擬機,生成機器碼文件

1. 代碼編譯 ,詞法語法語義分析,將java 原代碼編譯成字節(jié)碼文件;

我們的.java 文件最終會轉(zhuǎn)換為.class文件

2. 類加載機制,采用雙親委派避免重復(fù)加載(類名+類加載器),加載,驗證,準(zhǔn)備(準(zhǔn)備會對一些常量進(jìn)行初始化,遍歷初始化為0或null),解析,初始化

我們知道 JVM 虛擬機的入口就是類加載器,在加載java中的類時采用雙親委托機制,可以防止用戶新寫的類,替代jre中的類。

當(dāng)一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父 類去完 成,每一個層次類加載器都是如此,因此所有的加載請求都應(yīng)該傳送到啟動類加載其中, 只有當(dāng)父類 加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的 Class), 子類加 載器才會嘗試自己去加載。

類加載器有,啟動(Bootstrap)類加載器,擴展(Extension)類加載器,系統(tǒng)(System)類加載器,自定義類加載器

我們的Main類由系統(tǒng)類加載器進(jìn)行加載,委托給父類加載器

在JVM中表示兩個class對象是否為同一個類對象存在兩個必要條件
類的完整類名必須一致,包括包名。
加載這個類的ClassLoader(指ClassLoader實例對象)必須相同

hw.PNG

詳細(xì)的類加載過程是:

JVM 類加載機制分為五個部分:加載,驗證,準(zhǔn)備,解析,初始化。

  1. 加載, 這個階段會在內(nèi)存中生成一個代表這個類的 java.lang.Class 作為方法區(qū)這個類的各種數(shù) 據(jù)的入口。
  2. 驗證,確保 Class文件的字節(jié)流中包含的信息是否符合當(dāng)前虛擬機的要求
  3. 準(zhǔn)備,是正式為類變量分配內(nèi)存并設(shè)置類變量的初始值階段,public static int v = 8080,實際 上變量 v 在準(zhǔn)備階段過后的初始值為 0 而不是 8080,但是如果聲明的是常量就是8080,例如 public static final int v = 8080。
    4.解析,解析階段是指虛擬機將常量池中的符號引用替換為直接引用的過程。

3. 在jvm內(nèi)存中棧和本地線程棧是線程私有的,會在線程中創(chuàng)建一個main方法的棧幀

在main方法棧幀中,里面S,S2存放在其中的字符變量表中,然后“helloword”是一個字符串常量,在jvm中會存放在方法區(qū)中的字符常量池中的,然后將其從常量池中彈到操作數(shù)??臻g,然后賦值到s的空間中。這個 new String("helloword"), 先指向堆空間,堆空間再去指向 方法區(qū)常量池中。復(fù)制一份常量數(shù)據(jù)再放到s2 對應(yīng)的寄存器空間中。然后字面變量在字符變量表中指向這些空間。

具體可以使用 javap -c Main.class 解析.class 文件

Compiled from "Main.java"
public class com.bupt.learn.Main {
  public com.bupt.learn.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       // 將 helloword 從常量池推送到棧頂
       0: ldc           #2                  // String helloWorld
       // 將棧頂 引用 型數(shù)值存入第二個局部變量
       2: astore_1
       // 堆中創(chuàng)建一個對象,并將其引用值壓入棧頂
       3: new           #3                  // class java/lang/String
       // 復(fù)制棧頂數(shù)值并將復(fù)制值壓入棧頂
       6: dup
       //  將 helloword 從常量池推送到棧頂
       7: ldc           #2                  // String helloWorld
       // 調(diào)用超類構(gòu)造方法,實例初始化方法,私有方法
       9: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
       // 將棧頂 引用 型數(shù)值存入第三個局部變量
      12: astore_2
       // 獲取指定類的靜態(tài)字段,并將其壓入棧頂
      13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      // 將第二個 引用 型局部變量推送至棧頂
      16: aload_1
       // 調(diào)用實例方法
      17: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      23: aload_2
      24: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return
}

jvm.PNG
4. System.out.println(s):系統(tǒng)加載System.class字節(jié)碼文件到方法區(qū),并且系統(tǒng)會默認(rèn)在堆區(qū)創(chuàng)建System.out、System.in、System.err三個對象。

字符串在被輸出時會自動調(diào)用toString()方法。

println 是 java 原始的IO包,是使用 BIO 進(jìn)行輸出

一次I/O的完成的步驟

當(dāng)進(jìn)程發(fā)起系統(tǒng)調(diào)用時,這個系統(tǒng)調(diào)用就進(jìn)入內(nèi)核模式,然后開始I/O操作

I/O操作分為兩個步驟;

1、磁盤把數(shù)據(jù)裝載到內(nèi)核的內(nèi)存空間,

2、內(nèi)核的內(nèi)存空間的數(shù)據(jù)copy到用戶的內(nèi)存空間中(此過程是I/O發(fā)生的地方)

   阻塞:進(jìn)程發(fā)起I/O調(diào)用,進(jìn)程又不得不等待I/O的完成,此時CPU把進(jìn)程切換出去,進(jìn)程處于睡眠狀態(tài)則此過程為阻塞I/O

阻塞I/O系統(tǒng)怎么通知進(jìn)程?

   I/O完成,系統(tǒng)直接通知進(jìn)程,則進(jìn)程被喚醒
BIO.PNG
5. JVM 字節(jié)碼執(zhí)行引擎 生成機器節(jié)碼文件,執(zhí)行系統(tǒng)找到Main方法為其分配cpu進(jìn)行執(zhí)行;
6. cpu的執(zhí)行分為取值,譯碼,執(zhí)行 操作系統(tǒng)開始執(zhí)行指令失敗,缺中斷發(fā)送; 操作系統(tǒng)分配一頁內(nèi)存,將代碼從磁盤讀入,繼續(xù)執(zhí)行
7. 程序執(zhí)行系統(tǒng)調(diào)用,在文件描述符中寫一字符串
8. 操作系統(tǒng)檢查字符串的位置是否正確,操作系統(tǒng)找到字符串被送往的設(shè)備
9. 設(shè)備是一個偽終端,又一個進(jìn)程控制,操作系統(tǒng)將字符串送給該進(jìn)程,該進(jìn)程告訴窗口系統(tǒng)它要顯示字符串
10. 窗口系統(tǒng)確定這是一個合法的操作,然后將字符串轉(zhuǎn)成像素
11. 視頻硬件將成像素表示轉(zhuǎn)換成一組模擬信號控制顯示器在(重畫屏幕)
12 . 顯示器發(fā)射電子束,你在屏幕上看到“hello world”
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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