一式撼、前言
為什么要學習了解Java虛擬機
1.我們需要更加清楚的了解Java底層是如何運作的终抽,有利于我們更深刻的學習好Java纪他。
2.對我們調(diào)試錯誤提供很寶貴的經(jīng)驗。
3.這是合格的Java程序必須要了解的內(nèi)容耻煤。
二具壮、Java虛擬機的定義
Java虛擬機(Java Virtual Machine)准颓,簡稱JVM。當我們說起Java虛擬機時棺妓,可能指的是如下三種不同的東西:
1.抽象規(guī)范瞬场。
2.一個具體的實現(xiàn)。
3.一個運行中的虛擬機實例涧郊。
Java虛擬機抽象規(guī)范僅僅是一個概念,在《The Java Virtual Machine Specification》(java虛擬機規(guī)范)中有詳細的描述眼五。該規(guī)范的實現(xiàn)妆艘,可能來自多個提供商,并存在于多個平臺上看幼,它或者是全部由軟件實現(xiàn)批旺,或者是以硬件和軟件相結(jié)合的方式來實現(xiàn)。當運行一個Java程序的時候诵姜,也就在運行一個Java虛擬機實例汽煮。注意,我們所說的Java平臺無關(guān)性是指class文件的平臺無關(guān)性棚唆,JVM是和平臺相關(guān)的暇赤,不同操作系統(tǒng)對應(yīng)不同的JVM。
三宵凌、Java虛擬機的總體框架圖
下圖是整個Java虛擬機的總體框架圖鞋囊,之后我們會經(jīng)常涉及到。
四瞎惫、Java虛擬機的體系結(jié)構(gòu)
下圖表示了Java虛擬機的結(jié)構(gòu)框圖溜腐,主要描述了JVM子系統(tǒng)和內(nèi)存區(qū)。
五瓜喇、Java虛擬機各組成部分
5.1 類裝載子系統(tǒng)
類裝載子系統(tǒng)負責查找并裝載類型挺益,Java虛擬機由兩種類裝載器:啟動類裝載器(Java虛擬機實現(xiàn)的一部分)和用戶自定義類裝載器(Java程序的一部分)。類裝載子系統(tǒng)負責定位和導(dǎo)入二進制class文件乘寒,并且保證導(dǎo)入類的正確性望众,為類變量分配并初始化內(nèi)存,以及幫助解析符號引用肃续。類裝載器必須嚴格按照如下順序進行類的裝載黍檩。
1) 裝載 -- 查找并裝載類型的二進制數(shù)據(jù)
2) 連接 -- 執(zhí)行驗證,準備始锚,以及解析(可選)刽酱,連接分為如下三個步驟
驗證 -- 確保被導(dǎo)入類型的正確性
準備 -- 為類變量分配內(nèi)存,并將其初始化為默認值
解析 -- 把類型中的符號引用轉(zhuǎn)換為直接引用
3) 初始化 -- 把類變量初始化為正確初始值
啟動類裝載器 -- Java虛擬機必須有一個啟動類裝載器瞧捌,用于裝載受信任的類棵里,如Java API的class文件润文。
用戶自定義類裝載器 -- 繼承自ClassLoader類,ClassLoader的如下四個方法殿怜,是通往Java虛擬機的通道典蝌。
1. protected final Class defineClass(String name, byte data[], int offset, int length);
2. protected final Class defineClass(String name, byte data[], int offset, int length, ProtectionDomain protectionDomain);
3. protected final Class findSystemClass(String name);
4. protected final void resolveClass(Class c);
這四個方法涉及到了裝載和連接兩個階段,defineClass方法data參數(shù)為二進制Java Class文件格式头谜,表示一個新的可用類型骏掀,之后把這個類型導(dǎo)入到方法區(qū)中。findSystemClass的參數(shù)為全限定名柱告,通過類裝載器進行裝載截驮。resolveClass參數(shù)為Class實例,完成連接初始化操作际度。
5.2 方法區(qū)
方法區(qū)是線程共享的內(nèi)存區(qū)域葵袭,用于存儲已被虛擬機加載的類信息、常量乖菱、靜態(tài)變量坡锡、即時編譯器編譯后的代碼等數(shù)據(jù),當方法區(qū)無法滿足內(nèi)存分配需求時窒所,將拋出OutOfMemoryError(內(nèi)存溢出)鹉勒。
類信息包括
1.類型全限定名。
2.類型的直接超類的全限定名(除非這個類型是java.lang.Object吵取,它沒有超類)贸弥。
3.類型是類類型還是接口類型。
4.類型的訪問修飾符(public海渊、abstract绵疲、final的某個子集)。
5.任何直接超接口的全限定名的有序列表臣疑。
6.類型的常量池盔憨。
7.字段信息。
8.方法信息讯沈。
9.除了常量以外的所有類(靜態(tài))變量郁岩。
10.一個到類ClassLoader的引用。
11.一個到Class類的引用缺狠。
著重介紹常量池 -- 虛擬機必須要為每個被裝載的類型維護一個常量池问慎。常量池就是該類型所用常量的一個有序集合,包括直接常量和對其他類型挤茄、字段和方法的符號引用如叼。它在Java程序的動態(tài)連接中起著核心作用。
5.3 堆
一個虛擬機實例只對應(yīng)一個堆空間穷劈,堆是線程共享的笼恰。堆空間是存放對象實例的地方踊沸,幾乎所有對象實例都在這里分配。堆也是垃圾收集器管理的主要區(qū)域社证。堆可以處于物理上不連續(xù)的內(nèi)存空間中逼龟,只要邏輯上相連就行,當堆中沒有足夠的內(nèi)存進行對象實例分配時,并且堆也無法擴展時追葡,會拋出OutOfMemoryError異常腺律。
5.4 程序計數(shù)器
每個線程擁有自己的程序計數(shù)器,線程之間的程序計數(shù)器互不影響宜肉。PC寄存器的內(nèi)容總是下一條將被執(zhí)行指令的"地址"疾渣,這里的"地址"可以是一個本地指針,也可以是在方法字節(jié)碼中相對于該方法起始指令的偏移量崖飘。如果該線程正在執(zhí)行一個本地方法,則程序計數(shù)器內(nèi)容為undefined杈女,此區(qū)域在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域朱浴。
5.5 Java棧
Java棧也是線程私有的,虛擬機只會對棧進行兩種操作茫因,以幀為單位的入棧和出棧喘鸟。每個方法在執(zhí)行時都會創(chuàng)建一個幀芋绸,并入棧,成為當前幀梁沧。棧幀由三部分組成:局部變量區(qū)、操作數(shù)棧蝇裤、幀數(shù)據(jù)區(qū)廷支。
局部變量區(qū)被組織為一個以字長為單位、從0開始計數(shù)的數(shù)組栓辜。字節(jié)碼指令通過從0開始的索引來使用其中的數(shù)據(jù)恋拍。類型為int、float藕甩、reference和return Address的值在數(shù)組中只占據(jù)一項施敢,而類型為byte、short和char的值存入時都會轉(zhuǎn)化為int類型狭莱,也占一項僵娃,而long、double則連續(xù)占據(jù)兩項腋妙。
關(guān)于局部變量區(qū)給出如下一個例子默怨。
View Code
可以看到類方法的首項中沒有隱含的this指針,而對象方法則會隱含this指針骤素。并且byte,char,short,boolean類型存入局部變量區(qū)的時候都會被轉(zhuǎn)化成int類型值先壕,當被存回堆或者方法區(qū)時瘩扼,才會轉(zhuǎn)化回原來的類型。
操作數(shù)棧被組織成一個以字長為單位的數(shù)組垃僚,它是通過標準的棧操作-入棧和出棧來進行訪問集绰,而不是通過索引訪問。入棧和出棧也會存在類型的轉(zhuǎn)化谆棺。
棧數(shù)據(jù)區(qū)存放一些用于支持常量池解析栽燕、正常方法返回以及異常派發(fā)機制的信息。即將常量池的符號引用轉(zhuǎn)化為直接地址引用改淑、恢復(fù)發(fā)起調(diào)用的方法的幀進行正常返回碍岔,發(fā)生異常時轉(zhuǎn)交異常表進行處理。
5.6 本地方法棧
訪問本地方式時使用到的棧朵夏,為本地方法服務(wù)蔼啦,本地方法區(qū)域也會拋出StackOverflowError(棧溢出)和OutOfMemoryError(內(nèi)存溢出)異常。
5.7 執(zhí)行引擎
用戶所編寫的程序如何表現(xiàn)正確的行為需要執(zhí)行引擎的支持仰猖,執(zhí)行引擎執(zhí)行字節(jié)碼指令捏肢,完成程序的功能。后面會詳細介紹饥侵。
5.8 本地方法接口
本地方法接口稱為JNI鸵赫,是為可移植性準備的。