java中的Jvm原理

作為一名Java使用者跨蟹,掌握J(rèn)VM的體系結(jié)構(gòu)也是必須的近刘。
說起Java擒贸,人們首先想到的是Java編程語言,然而事實(shí)上觉渴,Java是一種技術(shù)酗宋,它由四方面組成:Java編程語言、Java類文件格式疆拘、Java虛擬機(jī)和Java應(yīng)用程序接口(Java API)蜕猫。它們的關(guān)系如下圖所示:


運(yùn)行期環(huán)境代表著Java平臺(tái),開發(fā)人員編寫Java代碼(.java文件)哎迄,然后將之編譯成字節(jié)碼(.class文件)回右,再然后字節(jié)碼被裝入內(nèi)存,一旦字節(jié)碼進(jìn)入虛擬機(jī)漱挚,它就會(huì)被解釋器解釋執(zhí)行翔烁,或者是被即時(shí)代碼發(fā)生器有選擇的轉(zhuǎn)換成機(jī)器碼執(zhí)行。

Java平臺(tái)由Java虛擬機(jī)和Java應(yīng)用程序接口搭建旨涝,Java語言則是進(jìn)入這個(gè)平臺(tái)的通道蹬屹,用Java語言編寫并編譯的程序可以運(yùn)行在這個(gè)平臺(tái)上。這個(gè)平臺(tái)的結(jié)構(gòu)如下圖所示:


在Java平臺(tái)的結(jié)構(gòu)中, 可以看出白华,Java虛擬機(jī)(JVM) 處在核心的位置慨默,是程序與底層操作系統(tǒng)和硬件無關(guān)的關(guān)鍵。它的下方是移植接口弧腥,移植接口由兩部分組成:適配器和Java操作系統(tǒng), 其中依賴于平臺(tái)的部分稱為適配器厦取;JVM 通過移植接口在具體的平臺(tái)和操作系統(tǒng)上實(shí)現(xiàn);在JVM 的上方是Java的基本類庫和擴(kuò)展類庫以及它們的API管搪, 利用Java API編寫的應(yīng)用程序(application) 和小程序(Java applet) 可以在任何Java平臺(tái)上運(yùn)行而無需考慮底層平臺(tái), 就是因?yàn)橛蠮ava虛擬機(jī)(JVM)實(shí)現(xiàn)了程序與操作系統(tǒng)的分離虾攻,從而實(shí)現(xiàn)了Java 的平臺(tái)無關(guān)性。

JVM在它的生存周期中有一個(gè)明確的任務(wù)更鲁,那就是運(yùn)行Java程序霎箍,因此當(dāng)Java程序啟動(dòng)的時(shí)候,就產(chǎn)生JVM的一個(gè)實(shí)例澡为;當(dāng)程序運(yùn)行結(jié)束的時(shí)候漂坏,該實(shí)例也跟著消失了。下面我們從JVM的體系結(jié)構(gòu)和它的運(yùn)行過程這兩個(gè)方面來對(duì)它進(jìn)行比較深入的研究。

1樊拓、Java虛擬機(jī)的體系結(jié)構(gòu)

·每個(gè)JVM都有兩種機(jī)制:

①類裝載子系統(tǒng):裝載具有適合名稱的類或接口

②執(zhí)行引擎:負(fù)責(zé)執(zhí)行包含在已裝載的類或接口中的指令

·每個(gè)JVM都包含:

方法區(qū)纠亚、Java堆、Java棧筋夏、本地方法棧蒂胞、指令計(jì)數(shù)器及其他隱含寄存器


對(duì)于JVM的學(xué)習(xí),在我看來這么幾個(gè)部分最重要:

Java代碼編譯和執(zhí)行的整個(gè)過程

JVM內(nèi)存管理及垃圾回收機(jī)制

下面分別對(duì)這幾部分進(jìn)行說明:
2条篷、Java代碼編譯和執(zhí)行的整個(gè)過程

也正如前面所說骗随,Java代碼的編譯和執(zhí)行的整個(gè)過程大概是:開發(fā)人員編寫Java代碼(.java文件),然后將之編譯成字節(jié)碼(.class文件)赴叹,再然后字節(jié)碼被裝入內(nèi)存鸿染,一旦字節(jié)碼進(jìn)入虛擬機(jī),它就會(huì)被解釋器解釋執(zhí)行乞巧,或者是被即時(shí)代碼發(fā)生器有選擇的轉(zhuǎn)換成機(jī)器碼執(zhí)行涨椒。

(1)Java代碼編譯是由Java源碼編譯器來完成,也就是Java代碼到JVM字節(jié)碼(.class文件)的過程绽媒。 流程圖如下所示:


(2)Java字節(jié)碼的執(zhí)行是由JVM執(zhí)行引擎來完成蚕冬,流程圖如下所示:


Java代碼編譯和執(zhí)行的整個(gè)過程包含了以下三個(gè)重要的機(jī)制:

·Java源碼編譯機(jī)制

·類加載機(jī)制

·類執(zhí)行機(jī)制
(1)Java源碼編譯機(jī)制

Java 源碼編譯由以下三個(gè)過程組成:

①分析和輸入到符號(hào)表

②注解處理

③語義分析和生成class文件

流程圖如下所示:


最后生成的class文件由以下部分組成:

①結(jié)構(gòu)信息:包括class文件格式版本號(hào)及各部分的數(shù)量與大小的信息

②元數(shù)據(jù):對(duì)應(yīng)于Java源碼中聲明與常量的信息。包含類/繼承的超類/實(shí)現(xiàn)的接口的聲明信息是辕、域與方法聲明信息和常量池

③方法信息:對(duì)應(yīng)Java源碼中語句和表達(dá)式對(duì)應(yīng)的信息囤热。包含字節(jié)碼、異常處理器表获三、求值棧與局部變量區(qū)大小旁蔼、求值棧的類型記錄、調(diào)試符號(hào)信息
(2)類加載機(jī)制
JVM的類加載是通過ClassLoader及其子類來完成的疙教,類的層次關(guān)系和加載順序可以由下圖來描述:


①Bootstrap ClassLoader

負(fù)責(zé)加載$JAVA_HOME中jre/lib/rt.jar里所有的class棺聊,由C++實(shí)現(xiàn),不是ClassLoader子類

②Extension ClassLoader

負(fù)責(zé)加載java平臺(tái)中擴(kuò)展功能的一些jar包松逊,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包

③App ClassLoader

負(fù)責(zé)記載classpath中指定的jar包及目錄中class

④Custom ClassLoader

屬于應(yīng)用程序根據(jù)自身需要自定義的ClassLoader躺屁,如tomcat肯夏、jboss都會(huì)根據(jù)j2ee規(guī)范自行實(shí)現(xiàn)ClassLoader

加載過程中會(huì)先檢查類是否被已加載经宏,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查驯击,只要某個(gè)classloader已加載就視為已加載此類烁兰,保證此類只所有ClassLoader加載一次。而加載的順序是自頂向下徊都,也就是由上層來逐層嘗試加載此類沪斟。

(3)類執(zhí)行機(jī)制

JVM是基于堆棧的虛擬機(jī)。JVM為每個(gè)新創(chuàng)建的線程都分配一個(gè)堆棧.也就是說,對(duì)于一個(gè)Java程序來說,它的運(yùn)行就是通過對(duì)堆棧的操作來完成的主之。堆棧以幀為單位保存線程的狀態(tài)择吊。JVM對(duì)堆棧只進(jìn)行兩種操作:以幀為單位的壓棧和出棧操作。

JVM執(zhí)行class字節(jié)碼槽奕,線程創(chuàng)建后几睛,都會(huì)產(chǎn)生程序計(jì)數(shù)器(PC)和棧(Stack),程序計(jì)數(shù)器存放下一條要執(zhí)行的指令在方法內(nèi)的偏移量粤攒,棧中存放一個(gè)個(gè)棧幀所森,每個(gè)棧幀對(duì)應(yīng)著每個(gè)方法的每次調(diào)用,而棧幀又是有局部變量區(qū)和操作數(shù)棧兩部分組成夯接,局部變量區(qū)用于存放方法中的局部變量和參數(shù)焕济,操作數(shù)棧中用于存放方法執(zhí)行過程中產(chǎn)生的中間結(jié)果。棧的結(jié)構(gòu)如下圖所示:


3盔几、JVM內(nèi)存管理及垃圾回收機(jī)制

JVM內(nèi)存結(jié)構(gòu)分為:方法區(qū)(method)晴弃,棧內(nèi)存(stack),堆內(nèi)存(heap)逊拍,本地方法棧(java中的jni調(diào)用)肝匆,結(jié)構(gòu)圖如下所示:


(1)堆內(nèi)存(heap)

所有通過new創(chuàng)建的對(duì)象的內(nèi)存都在堆中分配,其大小可以通過-Xmx和-Xms來控制顺献。
操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表旗国,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí),會(huì)遍歷該鏈表注整,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn)能曾,然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序肿轨,另外寿冕,對(duì)于大多數(shù)系統(tǒng),會(huì)在這塊內(nèi)存空間中的首地址處記錄本次分配的大小椒袍,這樣代碼中的delete語句才能正確的釋放本內(nèi)存空間驼唱。但由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中驹暑。這時(shí)由new分配的內(nèi)存玫恳,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片优俘,不過用起來最方便京办。另外,在WINDOWS下帆焕,最好的方式是用VirtualAlloc分配內(nèi)存惭婿,它不是在堆,也不是在棧,而是直接在進(jìn)程的地址空間中保留一塊內(nèi)存财饥,雖然這種方法用起來最不方便换吧,但是速度快,也是最靈活的钥星。堆內(nèi)存是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)式散,是不連續(xù)的內(nèi)存區(qū)域。由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的打颤,自然是不連續(xù)的暴拄,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存编饺。由此可見乖篷,堆獲得的空間比較靈活,也比較大透且。

(2)棧內(nèi)存(stack)

在Windows下, 棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)撕蔼,是一塊連續(xù)的內(nèi)存區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的秽誊,在WINDOWS下鲸沮,棧的大小是固定的(是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過棧的剩余空間時(shí)锅论,將提示overflow讼溺。因此,能從棧獲得的空間較小最易。只要棧的剩余空間大于所申請(qǐng)空間怒坯,系統(tǒng)將為程序提供內(nèi)存,否則將報(bào)異常提示棧溢出藻懒。 由系統(tǒng)自動(dòng)分配剔猿,速度較快。但程序員是無法控制的嬉荆。

堆內(nèi)存與棧內(nèi)存需要說明:

基礎(chǔ)數(shù)據(jù)類型直接在椆榫矗空間分配,方法的形式參數(shù)鄙早,直接在椡艏耄空間分配,當(dāng)方法調(diào)用完成后從椀妫空間回收陆爽。引用數(shù)據(jù)類型,需要用new來創(chuàng)建扳缕,既在棧空間分配一個(gè)地址空間,又在堆空間分配對(duì)象的類變量 躯舔。方法的引用參數(shù)驴剔,在棧空間分配一個(gè)地址空間粥庄,并指向堆空間的對(duì)象區(qū)丧失,當(dāng)方法調(diào)用完成后從棧空間回收惜互。局部變量new出來時(shí)布讹,在棧空間和堆空間中分配空間训堆,當(dāng)局部變量生命周期結(jié)束后描验,棧空間立刻被回收坑鱼,堆空間區(qū)域等待GC回收膘流。方法調(diào)用時(shí)傳入的literal參數(shù),先在椔沉ぃ空間分配呼股,在方法調(diào)用完成后從棧空間收回画恰。字符串常量彭谁、static在DATA區(qū)域分配,this在堆空間分配允扇。數(shù)組既在椔砜浚空間分配數(shù)組名稱,又在堆空間分配數(shù)組實(shí)際的大小蔼两。


(3)本地方法棧(java中的jni調(diào)用)

用于支持native方法的執(zhí)行甩鳄,存儲(chǔ)了每個(gè)native方法調(diào)用的狀態(tài)。對(duì)于本地方法接口额划,實(shí)現(xiàn)JVM并不要求一定要有它的支持妙啃,甚至可以完全沒有。Sun公司實(shí)現(xiàn)Java本地接口(JNI)是出于可移植性的考慮俊戳,當(dāng)然我們也可以設(shè)計(jì)出其它的本地接口來代替Sun公司的JNI揖赴。但是這些設(shè)計(jì)與實(shí)現(xiàn)是比較復(fù)雜的事情,需要確保垃圾回收器不會(huì)將那些正在被本地方法調(diào)用的對(duì)象釋放掉抑胎。

(4)方法區(qū)(method)

它保存方法代碼(編譯后的java代碼)和符號(hào)表燥滑。存放了要加載的類信息、靜態(tài)變量阿逃、final類型的常量铭拧、屬性和方法信息赃蛛。JVM用持久代(Permanet Generation)來存放方法區(qū),可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值搀菩。

垃圾回收機(jī)制

堆里聚集了所有由應(yīng)用程序創(chuàng)建的對(duì)象呕臂,JVM也有對(duì)應(yīng)的指令比如 new, newarray, anewarray和multianewarray,然并沒有向 C++ 的 delete肪跋,free 等釋放空間的指令歧蒋,Java的所有釋放都由 GC 來做,GC除了做回收內(nèi)存之外州既,另外一個(gè)重要的工作就是內(nèi)存的壓縮谜洽,這個(gè)在其他的語言中也有類似的實(shí)現(xiàn),相比 C++ 不僅好用吴叶,而且增加了安全性阐虚,當(dāng)然她也有弊端,比如性能這個(gè)大問題晤郑。

4敌呈、Java虛擬機(jī)的運(yùn)行過程示例

上面對(duì)虛擬機(jī)的各個(gè)部分進(jìn)行了比較詳細(xì)的說明,下面通過一個(gè)具體的例子來分析它的運(yùn)行過程造寝。

虛擬機(jī)通過調(diào)用某個(gè)指定類的方法main啟動(dòng)磕洪,傳遞給main一個(gè)字符串?dāng)?shù)組參數(shù),使指定的類被裝載诫龙,同時(shí)鏈接該類所使用的其它的類型析显,并且初始化它們。例如對(duì)于程序:


編譯后在命令行模式下鍵入: java HelloApp run virtual machine

將通過調(diào)用HelloApp的方法main來啟動(dòng)java虛擬機(jī)签赃,傳遞給main一個(gè)包含三個(gè)字符串"run"谷异、"virtual"、"machine"的數(shù)組〗趿模現(xiàn)在我們略述虛擬機(jī)在執(zhí)行HelloApp時(shí)可能采取的步驟歹嘹。

開始試圖執(zhí)行類HelloApp的main方法,發(fā)現(xiàn)該類并沒有被裝載孔庭,也就是說虛擬機(jī)當(dāng)前不包含該類的二進(jìn)制代表尺上,于是虛擬機(jī)使用ClassLoader試圖尋找這樣的二進(jìn)制代表。如果這個(gè)進(jìn)程失敗圆到,則拋出一個(gè)異常怎抛。類被裝載后同時(shí)在main方法被調(diào)用之前,必須對(duì)類HelloApp與其它類型進(jìn)行鏈接然后初始化芽淡。鏈接包含三個(gè)階段:檢驗(yàn)马绝,準(zhǔn)備和解析。檢驗(yàn)檢查被裝載的主類的符號(hào)和語義挣菲,準(zhǔn)備則創(chuàng)建類或接口的靜態(tài)域以及把這些域初始化為標(biāo)準(zhǔn)的默認(rèn)值富稻,解析負(fù)責(zé)檢查主類對(duì)其它類或接口的符號(hào)引用掷邦,在這一步它是可選的。類的初始化是對(duì)類中聲明的靜態(tài)初始化函數(shù)和靜態(tài)域的初始化構(gòu)造方法的執(zhí)行唉窃。一個(gè)類在初始化之前它的父類必須被初始化耙饰。整個(gè)過程如下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纹笼,一起剝皮案震驚了整個(gè)濱河市纹份,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌廷痘,老刑警劉巖蔓涧,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異笋额,居然都是意外死亡元暴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門兄猩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茉盏,“玉大人,你說我怎么就攤上這事枢冤○蹋” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵淹真,是天一觀的道長讶迁。 經(jīng)常有香客問我,道長核蘸,這世上最難降的妖魔是什么巍糯? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮客扎,結(jié)果婚禮上祟峦,老公的妹妹穿的比我還像新娘。我一直安慰自己徙鱼,他們只是感情好宅楞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疆偿,像睡著了一般咱筛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杆故,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天迅箩,我揣著相機(jī)與錄音,去河邊找鬼处铛。 笑死饲趋,一個(gè)胖子當(dāng)著我的面吹牛拐揭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奕塑,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堂污,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了龄砰?” 一聲冷哼從身側(cè)響起盟猖,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎换棚,沒想到半個(gè)月后式镐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡固蚤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年娘汞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夕玩。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡你弦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出燎孟,到底是詐尸還是另有隱情禽作,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布缤弦,位于F島的核電站领迈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏碍沐。R本人自食惡果不足惜狸捅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望累提。 院中可真熱鬧尘喝,春花似錦、人聲如沸斋陪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽无虚。三九已至缔赠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間友题,已是汗流浹背嗤堰。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留度宦,地道東北人踢匣。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓告匠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親离唬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子后专,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容