這篇文章將為大家詳細(xì)講解有關(guān)Java程序是如何執(zhí)行的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
創(chuàng)新互聯(lián)為企業(yè)級客戶提高一站式互聯(lián)網(wǎng)+設(shè)計(jì)服務(wù),主要包括成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、app軟件開發(fā)、小程序設(shè)計(jì)、宣傳片制作、LOGO設(shè)計(jì)等,幫助客戶快速提升營銷能力和企業(yè)形象,創(chuàng)新互聯(lián)各部門都有經(jīng)驗(yàn)豐富的經(jīng)驗(yàn),可以確保每一個作品的質(zhì)量和創(chuàng)作周期,同時每年都有很多新員工加入,為我們帶來大量新的創(chuàng)意。Java程序執(zhí)行過程
步驟 1: 寫源代碼,源代碼將以.java的文件格式保存在電腦硬盤中。
步驟 2: 編譯器(compiler)檢查是否存在編譯期錯誤(例如缺少分號,關(guān)鍵字拼寫錯誤等)。若通過檢測,編譯器就會將源代碼翻譯成字節(jié)碼(bytecode),以.class的文件格式保存在電腦硬盤中。
步驟 3: 若要運(yùn)行此Java程序,JVM中會有一個叫類加載器(class loader)的內(nèi)置程序把字節(jié)碼從硬盤載入到正位于內(nèi)存中的JVM里去。
步驟 4: JVM中還有一個叫字節(jié)碼校驗(yàn)器(bytecode verifier)的內(nèi)置程序檢測是否存在運(yùn)行期錯誤(例如棧溢出)。若通過檢測,字節(jié)碼校驗(yàn)器就會將字節(jié)碼傳遞給解釋器(interpreter)。
步驟 5: 解釋器會對字節(jié)碼進(jìn)行逐行翻譯,將其翻譯成當(dāng)前所在系統(tǒng)可以理解的機(jī)器碼(machine code),
步驟 6:將機(jī)器碼交給操作系統(tǒng),操作系統(tǒng)會以main方法作為入口開始執(zhí)行程序。至此,一個Java程序就這樣運(yùn)行起來了。
細(xì)心的讀者可能注意到了,在流程圖中還涉及到一個叫JIT的東西在步驟中沒有被解釋。那么JIT編譯器(Just-In-Time Compiler)是如果參與進(jìn)程序的執(zhí)行過程中呢?讓我們來看以下兩個例子。
情況 1: 解釋器對代碼進(jìn)行逐行解釋,正如我們在步驟中所介紹的。
情況 2: 這是JIT編譯器參與進(jìn)Java執(zhí)行過程的情況,JIT編譯器會掃描所有代碼并對其進(jìn)行優(yōu)化。例如此時它發(fā)現(xiàn)最后一行代碼是重復(fù)多余的,就會將其移除,只傳遞前4行代碼給解釋器。這樣解釋器就能運(yùn)行地更快速高效,畢竟少了一行多余的代碼需要翻譯。
當(dāng)然,這只是JIT編譯器的優(yōu)化手段之一,不同公司設(shè)計(jì)的JIT編譯器對Java程序的運(yùn)行會有不同的優(yōu)化方式。此外需要知道的是,JIT編譯器并不是每次都會參與到執(zhí)行過程中來。
在步驟3中我們談到字節(jié)碼會被類加載器載入到內(nèi)存,那么載入之后JVM是如何對其進(jìn)行內(nèi)存管理的呢?
通常,在載入內(nèi)存后,一個Java程序所占用的內(nèi)存會被大致分為3塊區(qū)域:堆(heap),棧(stack)和方法區(qū)(method area)。
堆:存放new出來的東西。
棧:存放局部變量。
方法區(qū):類型信息,字段信息,常量池(constant pool),靜態(tài)變量,方法信息等。
public final class Student extends Object implements Serializable { // 1.類信息 // 2.對象字段信息 private String name; private int score; // 3.常量池 public final int id = 0; public final String gender = "male"; // 4.靜態(tài)變量 public static int a = 0; // 5.方法信息 public int getid() { return id; }}
PC寄存器:存放將要執(zhí)行的指令的地址。(因?yàn)闄C(jī)器的腦子不靈活,所以需要一塊專門的區(qū)域幫他記住執(zhí)行到哪一步,不然它會忘記)
本地方法棧:與JVM棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是JVM棧為Java方法服務(wù),而本地方法棧則是為使用到的Native方法服務(wù)。有的虛擬機(jī)(例如Sun HotSpot虛擬機(jī))甚至直接就把本地方法棧和虛擬機(jī)棧合二為一。
每個線程擁有各自獨(dú)立的(虛擬機(jī))棧、PC寄存器和本地方法棧。而堆和方法區(qū)則是所有線程共享的。
最后讓我們通過一個小例子來理解Java程序執(zhí)行時內(nèi)存的變化。
public class Person { int id; int age; Person(int id1, int age1) { id = id1; age = age1; } public static void main(String[] args) { Person Tom = new Person(1, 25); } }
首先,在stack中申請了一塊內(nèi)存,這塊內(nèi)存區(qū)域名字叫Tom,此時區(qū)域里存儲的內(nèi)容為null。
接著,調(diào)用Person的構(gòu)造方法,方法的參數(shù)屬于局部變量,因此在stack中有兩塊區(qū)域分別存放id1和age1。
通過構(gòu)造方法,可以new出來一個Person的對象,這個對象連帶著其成員變量會被存放在heap中。成員變量id和age的值由存放在stack中的局部變量id1和age1賦予。
最后,將這個對象的引用值(類似于地址)傳遞給Tom,通過引用值我們就可以找到這個對象。
(注意:位于stack中的id1和age1會隨著構(gòu)造方法調(diào)用的結(jié)束而消失,這里為了更好地表現(xiàn)全過程,因此保留在圖中。)
棧和堆的大小都是固定為一個默認(rèn)值的,它們作為jvm的參數(shù)設(shè)定好了,不同的jvm設(shè)定的參數(shù)不同,相應(yīng)的棧和堆的大小也就不同。
棧是運(yùn)行時的單位:里面存儲的信息都是跟當(dāng)前線程相關(guān)的,包括局部變量、程序運(yùn)行狀態(tài)、方法返回值等;而堆是存儲的單位:它只負(fù)責(zé)存儲對象。
當(dāng)一個方法調(diào)用結(jié)束后,方法里的局部變量會隨之消失,不會在stack中繼續(xù)占用空間。棧與堆的分離使得不同線程可以訪問同一個對象,這是一種有效的數(shù)據(jù)交互方式(共享內(nèi)存);此外也節(jié)省了空間,因?yàn)槎阎械墓蚕沓A亢途彺婵梢员凰袟TL問。
關(guān)于Java程序是如何執(zhí)行的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。