Java的運(yùn)行原理
在Java中引入了虛擬機(jī)的概念,即在機(jī)器和編譯程序之間加入了一層抽象的虛擬的機(jī)器。這臺(tái)虛擬的機(jī)器在任何平臺(tái)上都提供給編譯程序一個(gè)的共同的接口。編譯程序只需要面向虛擬機(jī),生成虛擬機(jī)能夠理解的代碼,然后由解釋器來(lái)將虛擬機(jī)代碼轉(zhuǎn)換為特定系統(tǒng)的機(jī)器碼執(zhí)行。在Java中,這種供虛擬機(jī)理解的代碼叫做字節(jié)碼(ByteCode),它不面向任何特定的處理器,只面向虛擬機(jī)。每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的。Java源程序經(jīng)過(guò)編譯器編譯后變成字節(jié)碼,字節(jié)碼由虛擬機(jī)解釋執(zhí)行,虛擬機(jī)將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機(jī)器上的機(jī)器碼,然后在特定的機(jī)器上運(yùn)行。
Java 虛擬機(jī) (JVM)
Java虛擬機(jī)(JVM)是Java VirtualMachine的縮寫,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能模擬來(lái)實(shí)現(xiàn)的。
java中,類加載器把一個(gè)類裝入JAVA虛擬機(jī)需要經(jīng)過(guò)三個(gè)步驟來(lái)完成:裝載、鏈接、初始化,其中鏈接又分來(lái)校驗(yàn)、準(zhǔn)備、解析過(guò)程
- 裝載:查找和導(dǎo)入.class文件
- 連接:檢查裝入.class文件的正確性,然后,java虛擬機(jī)為變量分配內(nèi)存,設(shè)置默認(rèn)值
- 初始化:把符號(hào)引用變成直接引用。
public class Main {
private static int size = 1;
public static void main(String args[]) {
User u = new User();
u.setName("李文水");
u.setPwd("159");
String name = u.getName();
String pwd = u.getPwd();
System.out.println("姓名:"+u.getName());
u=null;
}
}
class User {
private String name;
private String pwd;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Java虛擬機(jī)工作流程
1、裝載
描敘:Java虛擬機(jī)裝載指定的CLASS文件,
結(jié)果:形成這個(gè)CLASS類的實(shí)例對(duì)象
過(guò)程:java虛擬機(jī)使用類裝載器定位到相應(yīng)的CLASS文件,然后讀取這個(gè)CLASS文件(一個(gè)線性二進(jìn)制數(shù)據(jù)流),將它傳入java虛擬機(jī)中。緊接著虛擬機(jī)提取其中的類型信息。比如:該類的類名,方法名,變量名,修飾符,方法的返回類型等等。還有一個(gè)重要的東西就是常量池。(常量池保存了該類型的所有常量,包括直接常量和對(duì)其他類型,字段,方法的符號(hào)引用)將這些信息保存在一個(gè)叫做方法區(qū)的地方。最終形成CLASS類的實(shí)例,這個(gè)實(shí)例存放在內(nèi)存的堆區(qū)。它成為了java程序與內(nèi)部數(shù)據(jù)結(jié)構(gòu)之間的接口,程序要訪問(wèn)該類型的信息,程序就調(diào)用該類型對(duì)應(yīng)的CLASS實(shí)例對(duì)象的方法。簡(jiǎn)而言之:這個(gè)過(guò)程就是把一個(gè)類型的二進(jìn)制數(shù)據(jù)解析為方法區(qū)中的內(nèi)部數(shù)據(jù)結(jié)構(gòu),并在堆上建立一個(gè)CLASS對(duì)象的過(guò)程。
示例:裝載Main類
Java虛擬機(jī)讀取Main類的CLASS文件,生產(chǎn)對(duì)應(yīng)的java.lang.Class類的實(shí)例,讀取其中的類型信息,比如修飾符private,public,static,另外變量size,name,pwd,User(User即為一個(gè)引用)共同構(gòu)成了這個(gè)類的常量池。將這些信息保存在方法區(qū),
2、連接
描述:驗(yàn)證,準(zhǔn)備,解析(可選)
結(jié)果:這個(gè)類型是正確的。(這里不知道該怎么描述)
過(guò)程:
1)驗(yàn)證:確定類型符合java語(yǔ)言的語(yǔ)義,比如:final類不能有子類,final方法不能被覆蓋,確保在類型和超類型之間沒有不兼容的方法聲明(比如兩個(gè)方法擁有同樣的名字,參數(shù)完全相同,但返回類型不同)。
2)準(zhǔn)備:java虛擬機(jī)為類變量分配內(nèi)存,設(shè)置默認(rèn)值
3)解析:在類型的常量池中尋找類,接口,字段和方法的符合引用把這些符號(hào)引用替換成直接引用的過(guò)程。
示例: 連接Main類
Java虛擬機(jī)為size分配內(nèi)存,并賦默認(rèn)值0.找到常量池中User類的引用,如果User類還沒有被裝載,則裝載并且連接該類,然后將常量池中對(duì)User類的引用替換為直接引用。在此時(shí)User類并不會(huì)被初始化,因?yàn)檫€沒有用它。
3、初始化
描述:初始化一些靜態(tài)變量
結(jié)果:這個(gè)類型可以使用了
過(guò)程:可能會(huì)調(diào)用()方法,(這個(gè)方法只能夠由java虛擬機(jī)調(diào)用)來(lái)初始化該類的靜態(tài)變量。在調(diào)用這個(gè)方法前,必須確認(rèn)該類的超類的()方法已經(jīng)被調(diào)用。
示例:初始化Main類
Java虛擬機(jī)將Main類的靜態(tài)變量賦值為1.
4、執(zhí)行
- User u = new User();(存放在內(nèi)存的堆區(qū))
創(chuàng)建了一個(gè)User類實(shí)例,實(shí)際上是通過(guò)這個(gè)類的CLASS實(shí)例實(shí)例化的。方法如下:User u=(User)Class.forName("User").newInstance();
為了方便,用C代替Class.forName("User")
- u.setName("李文水"); u.setPwd("159");
調(diào)用該類的方法,為該類的變量賦值,Java虛擬機(jī)內(nèi)部調(diào)用是這樣的,通過(guò)方法區(qū)找到該方法,利用CLASS實(shí)例的如下方法調(diào)用:
c.getMethod("setName").invoke(u,"李文水");
- String name = u.getName(); String pwd = u.getPwd();
與第二步類似,不同的是將取得的值分別賦給了變量name和pwd。關(guān)鍵是這個(gè)值保存在哪里?和實(shí)例對(duì)象一樣,存放在堆區(qū)。這個(gè)時(shí)候我應(yīng)該可以看出CLASS實(shí)例的作用了,它就是起個(gè)中間作用,將程序中的調(diào)用反應(yīng)到堆區(qū)上數(shù)據(jù)的變化。
- u = null;
這個(gè)步驟寫出來(lái)的目的是了解一下Java虛擬機(jī)垃圾回收機(jī)制。(沒有什么實(shí)際意義)