每個Java程序都運(yùn)行于某個具體的Java虛擬機(jī)實(shí)現(xiàn)的實(shí)例上大审。
當(dāng)啟動一個Java程序時余掖,一個虛擬機(jī)實(shí)例就誕生了,當(dāng)該程序關(guān)閉退出替废,這個虛擬機(jī)實(shí)例就隨之消亡。如果同一臺計(jì)算機(jī)上同時運(yùn)行3個Java程序泊柬,將得到3個Java虛擬機(jī)實(shí)例椎镣。每個程序都運(yùn)行于它自己的Java虛擬機(jī)實(shí)例中。
Java虛擬機(jī)實(shí)例通過調(diào)用某個初始類的main方法來運(yùn)行一個Java程序兽赁。這個main方法必須是public状答,static,接受String[]作為參數(shù)刀崖,返回void惊科。任何擁有這樣一個main方法的類都可以作為Java程序運(yùn)行的起點(diǎn)。
class Test{
public static void main(String[] args){
//
}
}
告訴Java虛擬機(jī)要運(yùn)行的Java程序中初始類的名字后亮钦,整個程序?qū)乃膍ain方法開始執(zhí)行馆截。
java Test Hello world
則args[0]="Hello"
,args[1]="world"
main方法是程序初始線程的起點(diǎn)蜂莉,任何其他線程都是由這個初始線程啟動的孙咪。
Java虛擬機(jī)內(nèi)部有兩種線程,非守護(hù)線程和守護(hù)線程巡语。
從main開始的初始線程是非守護(hù)線程翎蹈,只要還有任何非守護(hù)線程在運(yùn)行,這個Java程序就繼續(xù)運(yùn)行男公。當(dāng)程序中所有的非守護(hù)線程都終止時荤堪,虛擬機(jī)實(shí)例將自動退出合陵。
守護(hù)線程通常是由虛擬機(jī)自己使用,比如執(zhí)行垃圾收集任務(wù)的線程澄阳。Java程序也可以把它創(chuàng)建的任何線程標(biāo)記為守護(hù)線程拥知。
Java虛擬機(jī)體系結(jié)構(gòu)由4部分構(gòu)成:子系統(tǒng),內(nèi)存區(qū)碎赢,數(shù)據(jù)類型低剔,指令。
每個Java虛擬機(jī)都有一個類裝載器子系統(tǒng)肮塞,它根據(jù)給定的全限定名來裝入類型襟齿。
每個Java虛擬機(jī)都有一個執(zhí)行引擎,它負(fù)責(zé)執(zhí)行那些包含在被裝載的方法中的指令枕赵。
某些運(yùn)行時數(shù)據(jù)是由程序中的所有線程共享的猜欺,還有一些則只能由一個線程擁有。
方法區(qū)和堆拷窜,是由該虛擬機(jī)實(shí)例中所有線程共享的开皿。
虛擬機(jī)會從裝載的二進(jìn)制文件中解析類型信息放到方法區(qū)中,把該程序運(yùn)行時創(chuàng)建的對象都放到堆中篮昧。
每一個新線程被創(chuàng)建時赋荆,都將得到它自己的程序指針寄存器(PC)以及一個Java棧。
如果線程正在執(zhí)行的是一個Java方法(非本地方法)懊昨,那么PC指向下一條被執(zhí)行的指令窄潭,而Java棧總是存儲該線程中Java方法的調(diào)用狀態(tài)。
本地方法調(diào)用狀態(tài)疚颊,則是以某種依賴于具體實(shí)現(xiàn)的方式存儲在本地方法棧中狈孔,也可能是寄存器或者其他特定實(shí)現(xiàn)相關(guān)的內(nèi)存區(qū)中信认。
Java棧由許多幀(frame)組成材义,幀中包含了Java方法的調(diào)用狀態(tài)。當(dāng)線程調(diào)用一個Java方法時嫁赏,虛擬機(jī)壓入一個新的frame到該線程的Java棧中其掂,該方法返回時,這個frame從Java棧中彈出并拋棄潦蝇。
Java虛擬機(jī)為每一個線程創(chuàng)建的內(nèi)存區(qū)是私有的款熬,任何線程都不能訪問其他線程的PC或Java棧。
線程1攘乒,2正在執(zhí)行Java方法贤牛,它們的PC分別指向下一條將被執(zhí)行的指令。
線程3正在執(zhí)行一個本地方法则酝,它的PC值是不確定的殉簸。
Java虛擬機(jī)中的數(shù)據(jù)類型分為兩種:基本類型和引用類型。
基本類型的變量持有原始值,而引用類型的變量持有引用值般卑。
關(guān)于基本類型boolean武鲁,當(dāng)編譯器把Java源碼編譯為字節(jié)碼時,會用int或byte表示boolean蝠检。在Java虛擬機(jī)中沐鼠,false是用整數(shù)0來表示的,所有非零整數(shù)都是true叹谁。涉及boolean的操作則會使用int饲梭,boolean數(shù)組是當(dāng)做byte數(shù)組訪問的。
基本類型returnAddress本慕,是只在虛擬機(jī)內(nèi)部使用的基本類型排拷,用來實(shí)現(xiàn)Java程序中的finally字句。
其他基本類型都是數(shù)值類型锅尘,包括整數(shù)類型监氢,浮點(diǎn)數(shù)類型。
注:
基本類型的數(shù)據(jù)不能看作對象藤违,存放在棧中浪腐。基本類型都對應(yīng)有包裝類顿乒,包裝類就是對象了议街,分配在堆中,棧中保存的是堆內(nèi)對象的引用璧榄。引入包裝類的目的是為了讓這些數(shù)據(jù)具有對象性質(zhì)特漩,可以調(diào)用對象的方法。
引用類型有3種:類類型骨杂,接口類型涂身,數(shù)組類型,還有一種特殊的引用值null搓蚪。
類裝載器子系統(tǒng)蛤售,負(fù)責(zé)查找并裝載類型。
Java虛擬機(jī)有兩種類裝載器:啟動類裝載器和用戶自定義類裝載器妒潭。前者是Java虛擬機(jī)實(shí)現(xiàn)的一部分悴能,后者是一個普通的Java對象。
由不同的類裝載器裝載的類雳灾,會放在虛擬機(jī)內(nèi)部的不同命名空間中漠酿。
對于每一個被裝載的類型,Java虛擬機(jī)都會在內(nèi)存堆中為它創(chuàng)建一個java.lang.Class
類的實(shí)例來代表該類型谎亩,而裝載的類型信息則都位于方法區(qū)炒嘲。
每個類裝載器都有自己的命名空間谈竿,其中維護(hù)著由它裝載的類型。所以摸吠,一個Java程序可以多次裝載具有同一個全限定名的類型误算,類型的全限定名不足以確定它在Java虛擬機(jī)中的唯一性胸墙。對于每一個被裝載的類型龟再,Java虛擬機(jī)都會記錄裝載它的類型裝載器印衔。
當(dāng)虛擬機(jī)裝載某個類型時,先使用類裝載器定位并讀取相應(yīng)的字節(jié)碼文件啼止,然后提取其中的類型信息道逗,將這些類型信息和類的靜態(tài)變量存儲到方法區(qū)中。
由于所有線程都共享方法區(qū)献烦,因此方法區(qū)中的數(shù)據(jù)訪問必須被設(shè)計(jì)為線程安全的滓窍。假設(shè)同時有兩個線程來訪問一個類,而這個類還沒有被裝載巩那,那么吏夯,只應(yīng)該有一個線程去裝載它,另一個線程等待即横。
在Java源碼中噪生,全限定名由類所屬的包的名稱加一個“.”再加上類名組成。
一個Java程序獨(dú)占一個Java虛擬機(jī)實(shí)例东囚,每個Java虛擬機(jī)實(shí)例都有自己的堆空間跺嗽,堆空間由Java程序的各線程共享。