- 博主簡介:想進大廠的打工人
- 博主主頁:@xyk:
- 所屬專欄:?JavaEE初階?
本文我們主要講解一下面試中常見的問題,如果想深入了解,請看一下《Java虛擬機規(guī)范》這本書
目錄
文章目錄
一、JVM簡介
二、JVM整體組成
2.1 運行時數(shù)據(jù)區(qū)組成
2.2 小結(jié)
三、JVM類加載
3.1 類加載過程
四、類加載什么時候會觸發(fā)
五、雙親委派模型
一、JVM簡介
JVM 是 Java Virtual Machine 的簡稱,意為 Java虛擬機。
虛擬機是指通過軟件模擬的具有完整硬件功能的、運行在一個完全隔離的環(huán)境中的完整計算機系統(tǒng),JVM是通過軟件模擬Java字節(jié)碼的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都進行了裁剪。
二、JVM整體組成
JVM整體組成可分為四個部分:
1.類加載器(ClassLoader)
2.運行時數(shù)據(jù)區(qū)(Runtime Data Area)
3.執(zhí)行引擎(Execution Engine)
4.本地庫接口(Native Interface)
各個組成部分的用途:
程序在執(zhí)行之前先要把java代碼轉(zhuǎn)換成字節(jié)碼(.class文件),JVM首先需要把字節(jié)碼通過一定的方式 類加載器(ClassLoader) 把文件加載到內(nèi)存中?運行時數(shù)據(jù)區(qū)(Runtime Data Area) ,而字節(jié)碼文件是JVM的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器?執(zhí)行引擎(Execution Engine) 將字節(jié)碼翻譯成底層系統(tǒng)指令再交由CPU去執(zhí)行,而這個過程中需要調(diào)用其他語言的接口?本地庫接口(Native Interface) 來實現(xiàn)整個程序的功能,這就是這4個主要組成部分的職責與功能。
2.1 運行時數(shù)據(jù)區(qū)組成
jvm的運行時數(shù)據(jù)區(qū),不同虛擬機實現(xiàn)可能略微有所不同,但都會遵從Java虛擬機規(guī)范,Java 8 虛擬機規(guī)范規(guī)定,Java虛擬機所管理的內(nèi)存將會包括以下幾個運行時數(shù)據(jù)區(qū)域:
1.程序計數(shù)器(Program Counter Register)
程序計數(shù)器是一塊較小的內(nèi)存空間,它可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器,記錄當前線程執(zhí)行到哪個指令。
特征:線程私有,由于JVM的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,也就是一個處理器都只會執(zhí)行一條線程中的指令。因此為了線程切換后能恢復到正確的執(zhí)行位置,每個線程都由獨立的程序計數(shù)器。
如果線程正在執(zhí)行Java中的方法,程序計數(shù)器記錄的就是正在執(zhí)行虛擬機字節(jié)碼指令的地址,如果是Native方法,這個計數(shù)器就為空(undefined),因此該內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定OutOfMemoryError的區(qū)域。
2.Java虛擬機棧(JVM Stacks)
Java虛擬機棧(Java Virtual Machine Stacks)描述的是Java方法執(zhí)行的內(nèi)存模型,每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法返回地址等信息,每個方法從調(diào)用直至執(zhí)行完成的過程,都對應(yīng)著一個棧幀在虛擬機棧中入棧到出棧的過程。
特性:線程私有(不絕對,局部變量可以互相訪問),它的生命周期和線程相同。
3.本地方法棧
本地方法棧(Native Method Stack)與虛擬機棧的作用是一樣的,只不過虛擬機棧是服務(wù)Java方法的,而本地方法棧是為虛擬機調(diào)用Native方法服務(wù)的。
4.堆區(qū)(Heap)
Java堆(Java Heap)是Java虛擬機中內(nèi)存最大的一塊,是被所有線程共享的。我們常見的 JVM 參數(shù)設(shè)置 -Xms10m 最小啟動內(nèi)存是針對堆的,-Xmx10m 最大運行內(nèi)存也是針對堆的
堆的作用:程序中創(chuàng)建的所有對象(new對象,類的成員變量)都在保存在堆中。
?關(guān)于新生代和老生代,后面再講~
5.方法區(qū)(元數(shù)據(jù)區(qū))
方法區(qū)(Methed Area)用于存儲已被虛擬機加載的類信息(類對象)、常量、靜態(tài)變量、即時編譯后的代碼等數(shù)據(jù)。
在《Java虛擬機規(guī)范中》把此區(qū)域稱之為“方法區(qū)”,而在 HotSpot 虛擬機的實現(xiàn)中,在 JDK 7 時此區(qū)域叫做永久代(PermGen),JDK 8 中叫做元空間(Metaspace)。
PS:永久代(PermGen)和元空間(Metaspace)是 HotSpot 中對《Java虛擬機規(guī)范》中方法區(qū)的實現(xiàn),它們?nèi)咧g的關(guān)系就好比,對于一輛汽車來說它定義了一個部分叫做“動能提供裝置”,但對于不同的汽車有不同的實現(xiàn)技術(shù),比如對于燃油車來說,它的“動能提供裝置”的實現(xiàn)技術(shù)就是汽油發(fā)動機(簡稱發(fā)動機),而對于電動汽車來說,它的“動能提供裝置”的實現(xiàn)就是電動發(fā)動機(簡稱電機),發(fā)動機和電機就相當于永久代和元空間一樣,它是對于“制動器”也就是方法區(qū)定義的實現(xiàn)。
運行時常量池是方法區(qū)的一部分,存放字面量與符號引用:
- 字面量 : 字符串(JDK 8 移動到堆中) 、final常量、基本數(shù)據(jù)類型的值。
- 符號引用 : 類和結(jié)構(gòu)的完全限定名、字段的名稱和描述符、方法的名稱和描述符。
2.2 小結(jié)
記住這幾個原則:
- 局部變量在棧上
- 普通成員變量在堆上
- 靜態(tài)成員變量,基本數(shù)據(jù)類型的值,final常量,在方法區(qū)/元數(shù)據(jù)區(qū)上
三、JVM類加載
3.1 類加載過程
對于一個類來說,它的生命周期是這樣的:
主要分為這幾個步驟:
1. 加載:找到.class文件,并且讀文件內(nèi)容到內(nèi)存中
2. 連接:
? ? ? ? 2.1. 驗證:檢查.class文件格式是否正確
? ? ? ? 2.2. 準備:給類對象分配內(nèi)存空間,內(nèi)存空間中的數(shù)據(jù)全是0
? ? ? ? 2.3. 解析:初始化字符串常量,將符號引用轉(zhuǎn)為直接引用
那么符號引用是什么?
符號引用以一組符號來描述所引用的目標。符號引用可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可,符號引用和虛擬機的布局無關(guān)。
3. 初始化:針對類對象進行初始化(調(diào)用構(gòu)造方法,初始化靜態(tài)成員,執(zhí)行靜態(tài)代碼塊,類要是有父類還需要加載父類)
四、類加載什么時候會觸發(fā)
不是JVM一啟動,就把所有的.class都加載了,整體是一個“懶加載“的策略,非必要,不加載~~
什么是叫做必要?
1.創(chuàng)建了這個類的實例
2.使用了這個類的靜態(tài)方法/靜態(tài)屬性
3.使用子類,會觸發(fā)父類的加載
一旦加載過后,后續(xù)不再重復加載~
五、雙親委派模型
雙親委派模型:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最 終都應(yīng)該傳送到最頂層的啟動類加載器中,只有當父加載器反饋自己無 法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去完成加載。
JVM中,加載類,需要用到一組特殊的模塊,類加載器~
在JVM中,內(nèi)置了三個 類加載器:
- BootStrap ClassLoader(java規(guī)范的標準庫)
- Extension ClassLoader(JVM擴展庫)
- Application?ClassLoader(加載用戶提供的第三方庫/項目中的類)
加載一個類的時候,先從Applicaton ClassLoader開始加載,但是先把任務(wù)交給父親Extension ClassLoader,Extension ClassLoader收到請求再交給自己的父親BootStrap?ClassLoader,由于BootStrap?ClassLoader沒有了父親,此時就自己來搜索自己負責的區(qū)域,如果搜索到,直接進行后續(xù)加載步驟,如果沒搜到,再交給自己孩子去處理,依此類推到Application?ClassLoader,當都沒有找到的時候,會拋出一個 ClassNotFound Exception.
為了避免bug,保證BootStrap?ClassLoader先加載,Application?ClassLoader后加載~
雙親委派模型優(yōu)點:文章來源:http://www.zghlxwxcb.cn/news/detail-545875.html
1. 避免重復加載類:比如 A 類和 B 類都有一個父類 C 類,那么當 A 啟動時就會將 C 類加載起來,那么在 B 類進行加載時就不需要在重復加載 C 類了。
2. 安全性:使用雙親委派模型也可以保證了 Java 的核心 API 不被篡改,如果沒有使用雙親委派模
型,而是每個類加載器加載自己的話就會出現(xiàn)一些問題,比如我們編寫一個稱為 java.lang.Object
類的話,那么程序運行的時候,系統(tǒng)就會出現(xiàn)多個不同的 Object 類,而有些 Object 類又是用戶
自己提供的因此安全性就不能得到保證了。文章來源地址http://www.zghlxwxcb.cn/news/detail-545875.html
到了這里,關(guān)于【JavaEE】JVM的組成及類加載過程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!