1. 運行流程的概覽圖
2. 具體流程
2.1 java源文件編譯為class字節碼
java代碼是運行在Java虛擬機上的。但是java是一門面向對象的高級語言,它不僅語法非常復雜,抽象程度也非常高,並不能直接運行在計算機硬件機器上。
因此,在運行Java程序之前,需要編譯器把代碼編譯成java虛擬機所能識別的指令程序,這就是Java字節碼,即class文件。
2.2 類加載器把字節碼加載到虛擬機的方法區
類加載:虛擬機把描述類的數據從 Class 文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的 Java 類型,這就是虛擬機的類加載機制。
在Class文件中描述的各種信息,需要被加載到虛擬機之后才能運行和使用。因此,需要把class字節碼文件加載到Java虛擬機來。
加載階段:
通過一個類的全限定名來獲取定義此類的二進制字節流。
將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
---加載階段完成后,這些二進制字節流按照虛擬機所需的格式存儲在方法區之中。
驗證階段:
為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,不會危害虛擬機的安全,Java虛擬機對輸入的字節流走驗證過程。
驗證階段包括四個階段:文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證。
文件格式驗證: 驗證字節流是否符合Class文件格式規范,如:是否以魔數0xCAFEBABE開頭。
元數據驗證: 對字節碼描述的信息進行語義分析,如:這個類的父類是否繼承了不允許被繼承的類(被final修飾的類);
字節碼驗證: 主要目的是通過數據流和控制流分析,確定程序語義是合法的、符合邏輯的。如:保證跳轉指令不會跳轉到方法體以外的字節碼指令上。
符號引用驗證: 發生在虛擬機將符號引用轉化為直接引用的時候,如:校驗符號引用中通過字符串描述的全限定名是否能找到對應的類。
2.3 運行時創建對象
Java是面向對象的編程語言,程序的運行是以對象為調用單位的。
- 字節碼文件加載到虛擬機的方法區后,在程序運行過程,通過 class字節碼文件創建與其對應的對象信息 。
- 創建對象的方式有:new關鍵字,反射等。
- Java堆內存是線程共享的區域,創建后的對象信息就保存在Java堆內存中。
2.4 方法調用,執行引擎解釋為機器碼
JVM的調用單位是對象,但是真正執行功能性的代碼還是對象上的方法。 在運行過程中,每當調用進入一個java方法,java虛擬機會在當前線程的java方法棧中生成一個棧幀,用以存放局部變量以及字節碼的操作數。方法棧內存是線程私有的,每個線程都有自己的方法棧。如果對應的方法是本地方法,則對應的就是本地方法棧。
2.5 解釋

即使編譯: 對於部分熱點代碼,將一個方法包含的所有字節碼翻譯成機器指令,以提高java虛擬機的運行效率。
即時編譯是建立經典的二八定律上,即20%代碼占據了80%的計算資源。
2.6 CPU執行指令
- Java程序被加載入內存后,指令也在內存中了。
- 指令的指令寄存器IP,指向下一條待執行指令的地址。
- CPU的控制單元根據IP寄存器的指向,將主存中的指令裝載到指令寄存器,這些加載的指令就是一串二進制碼,還需要譯碼器進行解碼。
- 解碼后,如果需要獲取操作數,則從內存中取數據,調用運算單元進行計算。
2.7 多線程切換上下文
CPU一通上電,就會周而復始從內存中獲取指令、譯碼、執行。
- 為了支持多任務,CPU 將執行時間這個資源划分成時間片,每個程序執行一段時間。
- java虛擬機的多線程是通過線程輪流切換分配處理執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對於多核處理器來說是一個內核)都只會執行一條程序中的指令。
- 假設當前線程在運行中,CPU分配的時間執行完了,總得保存運行過的結果信息吧,要不然白白浪費之前的工作了,因此,程序計數器(PC寄存器)作用體現出來了,它是一塊較小的內存空間,線程私有,可以看作當前線程執行的字節碼的行號指示器。當CPU又給它分配時間跑的時候,可以把數據恢復,接着上一次執行到的位置繼續執行就可以了。
