源碼到類文件
class Person{
private String name;
private int age;
private static String address;
private final static
String hobby="Programming";
public void say(){
System.out.println("person say...");
}
public int calc(int op1,int op2){
return op1+op2;
}
}
編譯: javac Person.java ---> Person.class
編譯過程:
Person.java -> 詞法分析器 -> tokens流 -> 語法分析器 -> 語法樹/抽象語法樹 -> 語義分析器
-> 註解抽象語法樹 -> 字節碼生成器 -> Person.class文件
類文件到虛擬機(類加載機制)
類加載機制流程:
虛擬機把Class文件加載到內存
並對數據進行校驗,轉換解析和初始化
形成可以虛擬機直接使用的Java類型,即java.lang.Class
1. 裝載(Load)
查找和導入class文件
(1)通過一個類的全限定名獲取定義此類的二進制字節流
(2)將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構
(3)在Java堆中生成一個代表這個類的java.lang.Class對象,作為對方法區中這些數據的訪問入口
2. 鏈接(Link)
2.1 驗證(Verify)
保證被加載類的正確性
文件格式驗證
元數據驗證
字節碼驗證
符號引用驗證
2.2 準備(Prepare)
為類的靜態變量分配內存,並將其初始化為默認值
2.3 解析(Resolve)
把類中的符號引用轉換為直接引用
3.初始化(Initialize)
對類的靜態變量,靜態代碼塊執行初始化操作
類裝載器ClassLoader
在裝載(Load)階段,其中第(1)步:通過類的全限定名獲取其定義的二進制字節流,需要藉助類裝載
器完成,顧名思義,就是用來裝載Class文件的。
(1)通過一個類的全限定名獲取定義此類的二進制字節流 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構 在Java堆中生成一個代表這個類的java.lang.Class對象,作為對方法區中這些數據的訪問入口
雙親委派機制
定義:如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把
這個請求任務委託給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務,就
成功返回;只有父類加載器無法完成此加載任務時,才自己去加載
JVM 運行時數據區
Method Area(方法區)
方法區是各個線程共享的內存區域,在虛擬機啟動時創建。
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
雖然Java虛擬機規範把方法區描述為堆的一個邏輯部分,但是它卻又一個別名叫做Non-Heap(非堆),目
的是與Java堆區分開來。
當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。
Heap(堆)
Java堆是Java虛擬機所管理內存中最大的一塊,在虛擬機啟動時創建,被所有線程共享。
Java對象實例以及數組都在堆上分配。
Java Virtual Machine Stacks(虛擬機棧)
虛擬機棧是一個線程執行的區域,保存著一個線程中方法的調用狀態。換句話說,一個Java線程的運行
狀態,由一個虛擬機棧來保存,所以虛擬機棧肯定是線程私有的,獨有的,隨著線程的創建而創建。
每一個被線程執行的方法,為該棧中的棧幀,即每個方法對應一個棧幀。
調用一個方法,就會向棧中壓入一個棧幀;一個方法調用完成,就會把該棧幀從棧中彈出。
The pc Register(程序計數器)
我們都知道一個JVM進程中有多個線程在執行,而線程中的內容是否能夠擁有執行權,是根據
CPU調度來的。
假如線程A正在執行到某個地方,突然失去了CPU的執行權,切換到線程B了,然後當線程A再獲
得CPU執行權的時候,怎麼能繼續執行呢?這就是需要在線程中維護一個變量,記錄線程執行到
的位置。
**程序計數器佔用的內存空間很小,由於Java虛擬機的多線程是通過線程輪流切換,並分配處理器執行時
間的方式來實現的,在任意時刻,一個處理器只會執行一條線程中的指令。因此,為了線程切換後能夠
恢復到正確的執行位置,每條線程需要有一個獨立的程序計數器(線程私有)。
如果線程正在執行Java方法,則計數器記錄的是正在執行的虛擬機字節碼指令的地址;
如果正在執行的是Native方法,則這個計數器為空**
Native Method Stacks(本地方法棧)
如果當前線程執行的方法是Native類型的,這些方法就會在本地方法棧中執行。