| 1、Java虛擬機(jī)是什么 “Java虛擬機(jī)“可以指三種不同的東西 抽象規(guī)范 一個(gè)具體的實(shí)現(xiàn) 一個(gè)運(yùn)行中的虛擬機(jī)實(shí)例 當(dāng)運(yùn)行一個(gè)Java程序的同時(shí),也就是在運(yùn)行一個(gè)Java虛擬機(jī)實(shí)例 2、Java虛擬機(jī)的生命周期 當(dāng)啟動(dòng)一個(gè)Java程序時(shí),一個(gè)虛擬機(jī)實(shí)例也就誕生了,當(dāng)該程序關(guān)閉退出時(shí),這個(gè)虛擬機(jī)實(shí)例也就隨之消亡。 在java虛擬機(jī)內(nèi)部有兩種線(xiàn)程:守護(hù)線(xiàn)程和非守護(hù)線(xiàn)程。當(dāng)該程序中所有的非守護(hù)線(xiàn)程都終止時(shí),虛擬機(jī)實(shí)例將自動(dòng)退出。 3、Java虛擬機(jī)的體系結(jié)構(gòu)   一個(gè)虛擬機(jī)實(shí)例的行為是分別按照子系統(tǒng)、內(nèi)存區(qū)、數(shù)據(jù)類(lèi)型以及指令這幾個(gè)術(shù)語(yǔ)來(lái)描述的。 3.1、數(shù)據(jù)類(lèi)型  Java語(yǔ)言中所有的基本類(lèi)型同樣也都是Java虛擬機(jī)中的基本類(lèi)型。但boolean有點(diǎn)特別,指令集對(duì)boolean只有很有限的支持。當(dāng)編譯器把Java源碼編譯為字節(jié)碼的時(shí),它會(huì)用int或byte來(lái)表示boolean。Boolean數(shù)組是當(dāng)byte數(shù)組來(lái)訪(fǎng)問(wèn)的。 returnAddress是Java虛擬機(jī)內(nèi)部使用的基本類(lèi)型,這個(gè)類(lèi)型被用來(lái)實(shí)現(xiàn)Java程序中的finally子句。 3.2、類(lèi)裝載器子系統(tǒng) 負(fù)責(zé)查找并裝載的那部分被稱(chēng)為類(lèi)裝載器子系統(tǒng)。 分為啟動(dòng)類(lèi)裝載器和用戶(hù)自定義類(lèi)裝載器 由不同的類(lèi)裝載器裝載的類(lèi)將放在虛擬機(jī)內(nèi)部的不同命名空間中。 用戶(hù)自定義的類(lèi)裝載器以及Class類(lèi)的實(shí)例都放在內(nèi)存的堆區(qū),而裝載的類(lèi)型信息則都位于方法區(qū)。 裝載順序: 1)裝載——查找并裝載類(lèi)型的二進(jìn)制數(shù)據(jù) 2)連接——執(zhí)行驗(yàn)證(確保被導(dǎo)入類(lèi)型的正確性),準(zhǔn)備(為類(lèi)變量分配內(nèi)存,并將其初始化為默認(rèn)值),以及解析(把類(lèi)變量中的符號(hào)引用轉(zhuǎn)換為正確的初始值) 3)初始化——把類(lèi)變量初始化為正確的初始值 3.3方法區(qū) 在java虛擬機(jī)中,關(guān)于被裝載類(lèi)型的信息存儲(chǔ)在一個(gè)邏輯上被稱(chēng)為方法區(qū)的內(nèi)存中。 所有線(xiàn)程都共享方法區(qū)。 類(lèi)型信息: 這個(gè)類(lèi)型的全限定名 這個(gè)類(lèi)型的直接超類(lèi)的全限定名 這個(gè)類(lèi)型是類(lèi)類(lèi)型還是接口類(lèi)型 這個(gè)類(lèi)型的訪(fǎng)問(wèn)修飾符 任何直接超接口的全限定名的有序列表 該類(lèi)型的常量池 字段信息 方法信息 除了常量以外的所有類(lèi)(靜態(tài))變量 一個(gè)到類(lèi)ClassLoader的引用 一個(gè)到Class類(lèi)的引用 其中字段信息包括 字段名 字段類(lèi)型 字段的修飾符 方法信息包括 方法名 方法的返回類(lèi)型 方法參數(shù)的數(shù)量和類(lèi)型 方法的修飾符 如果方法不是抽象的和本地的還須有 方法的字節(jié)碼 操作數(shù)棧和該方法的棧幀中的局部變量的大小 異常表 3.4 堆 Java程序在運(yùn)行時(shí)所創(chuàng)建的所有類(lèi)實(shí)例或數(shù)組都放在同一個(gè)堆中。 Java對(duì)象中包含的基本數(shù)據(jù)由它所屬的類(lèi)及其所有超類(lèi)聲明的實(shí)例變量組成。只要有一個(gè)對(duì)象引用,虛擬機(jī)就必須能快速的定位對(duì)象實(shí)例的數(shù)據(jù),另外,它必須能通過(guò)該對(duì)象引用訪(fǎng)問(wèn)相應(yīng)的類(lèi)數(shù)據(jù),因此對(duì)象中通常有一個(gè)指向方法區(qū)的指針。 一種可能的堆空間設(shè)計(jì)就是,把堆分為兩部分:一個(gè)句柄池,一個(gè)對(duì)象池。 這種設(shè)計(jì)的好處是有利于堆碎片的整理,缺點(diǎn)是每次訪(fǎng)問(wèn)對(duì)象的實(shí)例變量都需要經(jīng)過(guò)兩次指針傳遞。  另一種設(shè)計(jì)方式是使對(duì)象直接指向一組數(shù)據(jù),而數(shù)據(jù)包括對(duì)象實(shí)例數(shù)據(jù)以及指向方法區(qū)類(lèi)數(shù)據(jù)的指針。這種設(shè)計(jì)方式的優(yōu)點(diǎn)是只需要一個(gè)指針就可以訪(fǎng)問(wèn)對(duì)象的實(shí)例數(shù)據(jù),但是移動(dòng)對(duì)象就變得更加復(fù)雜。  堆中其他數(shù)據(jù): 1、對(duì)象鎖,用于協(xié)調(diào)多個(gè)線(xiàn)程訪(fǎng)問(wèn)一個(gè)對(duì)象時(shí)的同步。 2、等待集合 3、與垃圾收集器有關(guān)的數(shù)據(jù)。 4、方法表:加快了調(diào)用實(shí)例方法時(shí)的效率。 方法表指向的實(shí)例方法數(shù)據(jù)包括以下信息: 此方法的操作數(shù)棧和局部變量區(qū)的大小 此方法的字節(jié)碼 異常表 這些信息足夠虛擬機(jī)去調(diào)用一個(gè)方法了,方法表包含有方法指針——指向類(lèi)活或超類(lèi)聲明的方法的數(shù)據(jù)  3.5程序計(jì)數(shù)器 對(duì)于一個(gè)運(yùn)行中的Java程序而言,其中的每一個(gè)線(xiàn)程都有它自己的PC(程序計(jì)數(shù)器),在線(xiàn)程啟動(dòng)時(shí)創(chuàng)建。大小是一個(gè)字長(zhǎng)。當(dāng)線(xiàn)程執(zhí)行某個(gè)Java方法時(shí),PC的內(nèi)容總是下一條將被指向指令的“地址”。如果該線(xiàn)程正在執(zhí)行一個(gè)本地方法,那么此時(shí)PC的值為”undefined”。Java棧 3.6Java棧 每當(dāng)啟動(dòng)一個(gè)線(xiàn)程時(shí),Java虛擬機(jī)都會(huì)為它分配一個(gè)Java棧,Java棧也幀為單位保存線(xiàn)程的運(yùn)行狀態(tài),虛擬機(jī)只會(huì)直接對(duì)Java棧執(zhí)行兩種操作:以幀為單位的壓棧和出棧。 某個(gè)線(xiàn)程正在執(zhí)行的方法被稱(chēng)為該線(xiàn)程的當(dāng)前方法,當(dāng)前方法使用的棧幀稱(chēng)為當(dāng)前幀,當(dāng)前方法所屬的類(lèi)稱(chēng)為當(dāng)前類(lèi),當(dāng)前類(lèi)的常量池稱(chēng)為當(dāng)前常量池,在線(xiàn)程執(zhí)行一個(gè)方法時(shí),它會(huì)跟蹤當(dāng)前類(lèi)和當(dāng)前常量池。 每當(dāng)線(xiàn)程調(diào)用一個(gè)方法時(shí),虛擬機(jī)都會(huì)在該線(xiàn)程的Java棧中壓入一個(gè)新幀,而這個(gè)新棧自然就成為當(dāng)前幀。在執(zhí)行這個(gè)方法時(shí),它使用這個(gè)幀來(lái)存儲(chǔ)參數(shù)、局部變量、中間運(yùn)算結(jié)果等等數(shù)據(jù)。 Java棧上的所有數(shù)據(jù)都是數(shù)據(jù)都是此線(xiàn)程私有的。 3.7棧幀 棧幀由三部分組成:局部變量區(qū)、操作數(shù)棧和幀數(shù)據(jù)區(qū)。局部變量區(qū)和操作數(shù)棧的大小要視對(duì)應(yīng)的方法而定,編譯器在編譯器時(shí)就確定的確定了這些值并放在class文件中。幀數(shù)據(jù)區(qū)的大小依賴(lài)于具體的實(shí)現(xiàn)。 當(dāng)虛擬機(jī)調(diào)用一個(gè)方法時(shí),它從對(duì)應(yīng)類(lèi)的類(lèi)型信息中得到此方法的局部變量區(qū)和操作數(shù)棧的大小,并據(jù)此分配棧幀內(nèi)存,然后壓入Java棧中。 局部變量區(qū):Java棧幀的局部變量區(qū)被組織為以一個(gè)字長(zhǎng)為單位、從0開(kāi)始計(jì)數(shù)的數(shù)組。字節(jié)碼指令通過(guò)從0開(kāi)始的索引來(lái)使用其中的數(shù)據(jù)。 局部變量區(qū)對(duì)應(yīng)方法的參數(shù)和局部變量。編譯器首先按聲明的順序把這些參數(shù)放入局部變量數(shù)組。 在java中,所有的對(duì)象都按引用傳遞,并且都存儲(chǔ)在堆中,永遠(yuǎn)都不會(huì)在局部變量區(qū)或操作數(shù)棧中發(fā)現(xiàn)對(duì)象的拷貝,只會(huì)有對(duì)象的引用。 操作數(shù)棧:操作數(shù)棧也是被組織為一個(gè)字長(zhǎng)為單位的數(shù)組。但它不是通過(guò)索引來(lái)訪(fǎng)問(wèn),而是通過(guò)標(biāo)準(zhǔn)的棧操作——壓棧和出棧來(lái)訪(fǎng)問(wèn)的。 幀數(shù)據(jù)區(qū):支持解析常量池解析、正常方法返回以及異常派發(fā)機(jī)制。每當(dāng)虛擬機(jī)要執(zhí)行某個(gè)需要用到常量池?cái)?shù)據(jù)的指令時(shí),它都會(huì)通過(guò)幀數(shù)據(jù)區(qū)中指向常量池 的指針來(lái)訪(fǎng)問(wèn)它。常量池中對(duì)類(lèi)型、字段和方法的引用在開(kāi)始時(shí)都是符號(hào)。當(dāng)虛擬機(jī)在常量池中搜索時(shí),如果遇到類(lèi)、接口、字段或者方法的入口,假若它們?nèi)匀皇? 符號(hào),虛擬機(jī)那時(shí)候才會(huì)進(jìn)行解析。 3.8執(zhí)行引擎 指令集:方法的字節(jié)碼流是由Java虛擬機(jī)的指令序列構(gòu)成的。每一條指令包含一個(gè)單字節(jié)的操作碼,后面跟隨0個(gè)或多個(gè)操作數(shù)。操作碼本身就已經(jīng)規(guī)定了它是否需要跟隨操作數(shù),以及如果有操作數(shù)它是什么形式的。當(dāng)虛擬機(jī)執(zhí)行一條指令的時(shí)候,可能使用當(dāng)前常量池中的項(xiàng)、當(dāng)前幀的局部變量中的值,或者當(dāng)前幀操作數(shù)棧頂端的值。 執(zhí)行技術(shù):解釋、即時(shí)編譯、字適應(yīng)優(yōu)化、芯片級(jí)直接執(zhí)行。 Hotspot虛擬機(jī)就采用了自適應(yīng)優(yōu)化。自適應(yīng)優(yōu)化虛擬機(jī)開(kāi)始的時(shí)候?qū)λ械拇a都是解釋執(zhí)行,但是它會(huì)監(jiān)視代碼的執(zhí)行情況。大多數(shù)程序花費(fèi)80%-90%的時(shí)間來(lái)執(zhí)行10%-20%的代碼。虛擬機(jī)可以意識(shí)到那些方法是程序的熱區(qū)——就是那10%-20%的代碼,他們占整個(gè)執(zhí)行時(shí)間的80%-90%。當(dāng)自適應(yīng)優(yōu)化的虛擬機(jī)判斷出某個(gè)特定的方法是瓶頸的時(shí)候,它啟動(dòng)一個(gè)后臺(tái)線(xiàn)程,把字節(jié)碼編譯成本地代碼,非常仔細(xì)的優(yōu)化這些本地代碼。 | 
|  | 
來(lái)自: catph > 《java虛擬機(jī)筆記》