文章目錄了解JVM(Java虛擬機(jī))首先我們必須了解VM(虛擬機(jī))是什么。 所謂虛擬機(jī)(Virtual Machine),就是一臺(tái)虛擬的計(jì)算機(jī)。它是一款軟件,用來(lái)執(zhí)行一系列虛擬計(jì)算機(jī)指令。大體上,虛擬機(jī)可以分為系統(tǒng)虛擬機(jī)和程序虛擬機(jī)。 我們經(jīng)常見(jiàn)到的 VMware 就屬于系統(tǒng)虛擬機(jī),它是完全對(duì)物理計(jì)算機(jī)的仿真,提供了一個(gè)可運(yùn)行完整操作系統(tǒng)的軟件平臺(tái)。 程序虛擬機(jī)典型的代表就是 Java 虛擬機(jī)了,它專(zhuān)門(mén)為執(zhí)行某個(gè)單個(gè)計(jì)算機(jī)程序而設(shè)計(jì)。在 Java 虛擬機(jī)中執(zhí)行的指令我們稱(chēng)為 Java 字節(jié)碼指令。無(wú)論是系統(tǒng)虛擬機(jī)還是程序虛擬機(jī),在上面運(yùn)行的軟件都被限制于虛擬機(jī)提供的資源。Java 虛擬機(jī)是一種執(zhí)行 Java 字節(jié)碼文件的虛擬計(jì)算機(jī),它擁有獨(dú)立的運(yùn)行機(jī)制。 Java 技術(shù)的核心就是 Java 虛擬機(jī),因?yàn)樗械?Java 程序都運(yùn)行在 Java 虛擬機(jī)內(nèi)部。 JVM概述JVM就是Java虛擬機(jī),虛擬機(jī)就是一臺(tái)虛擬的計(jì)算機(jī),是一款軟件。Java 虛擬機(jī)就是二進(jìn)制字節(jié)碼的運(yùn)行環(huán)境,負(fù)責(zé)裝載字節(jié)碼到其內(nèi)部,解釋或編譯為對(duì)應(yīng)平臺(tái)上的機(jī)器碼指令執(zhí)行,每一條 Java 指令,Java 虛擬機(jī)中都有詳細(xì)定義,如怎么取操作數(shù),怎么處理操作數(shù),處理結(jié)果放在哪兒等。JVM是運(yùn)行在操作系統(tǒng)上的,不與硬件直接交互。 JVM整體的四個(gè)部分JVM整體組成可以分為4個(gè)部分:
程序在執(zhí)行之前先要把 Java 代碼轉(zhuǎn)換成字節(jié)碼(.class 文件),JVM 首先需要把字節(jié)碼通過(guò)一定的類(lèi)加載器(Class Loader)把文件加載到內(nèi)存中運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area),而字節(jié)碼文件是 JVM 的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器(執(zhí)行引擎(Execution Engine)) 將字節(jié)碼翻譯成底層系統(tǒng)指令再交由 CPU 去執(zhí)行,而這個(gè)過(guò)程中需要調(diào)用其他語(yǔ)言的接口(本地方法接口(Native Interface)) 來(lái)實(shí)現(xiàn)。整個(gè)程序的功能,這就是這 4 個(gè)主要組成部分的職責(zé)與功能。 1.1 類(lèi)加載器
1.1.1 類(lèi)加載器過(guò)程類(lèi)加載器子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載 class 文件, class 文件在文件開(kāi)頭有特定的文件標(biāo)識(shí)(字節(jié)碼文件都以 CA FE BA BE 標(biāo)識(shí)開(kāi)頭)。classLoader 只負(fù)責(zé) class 文件的加載,至于它是否可以運(yùn)行,則由執(zhí)行引擎決定。加載的類(lèi)信息存放于一塊稱(chēng)為方法區(qū)的內(nèi)存空間。除了類(lèi)的信息外,方法區(qū)中還會(huì)存放運(yùn)行時(shí)常量池信息,可能還包括字符串字面量和數(shù)字常量(這部分常量信息是 class 文件中常量池部分的內(nèi)存映射)。 類(lèi)加載的過(guò)程 1.加載過(guò)程是把class文件(字節(jié)碼文件)加載到內(nèi)存中(I/O讀寫(xiě))。類(lèi)加載器把文件加載到內(nèi)存中,會(huì)為每個(gè)類(lèi)創(chuàng)建一個(gè)Class類(lèi)的對(duì)象,調(diào)用Class類(lèi)中的方法獲取類(lèi)的相關(guān)信息。 2.驗(yàn)證是檢驗(yàn)加載的類(lèi)是否有正確的內(nèi)部結(jié)構(gòu)并和其他類(lèi)協(xié)調(diào)一致。 3.準(zhǔn)備階段為類(lèi)的靜態(tài)屬性分配內(nèi)存,并設(shè)置默認(rèn)初始值(不包含final修飾的static常量),也不會(huì)給實(shí)例變量初始化。 4.解析是將二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用(符號(hào)引用是用一組符號(hào)描述所引用的目標(biāo);直接引用是指向目標(biāo)的指針)。 5.類(lèi)初始化 ? 5.1 什么時(shí)候初始化類(lèi) ? 1 )創(chuàng)建類(lèi)的實(shí)例,也就是 new 一個(gè)對(duì)象 ? 5.2 類(lèi)的初始化順序: ? 父類(lèi)static --> 子類(lèi)static --> 父類(lèi)構(gòu)造方法 --> 子類(lèi)構(gòu)造方法 1.1.2類(lèi)加載器的分類(lèi)1.1.2.1啟動(dòng)類(lèi)加載器(引導(dǎo)類(lèi)加載器)是由c/c++實(shí)現(xiàn),用來(lái)加載Java的核心類(lèi)庫(kù) 1.1.2.2擴(kuò)展類(lèi)加載器由Java實(shí)現(xiàn),派生于 ClassLoader 類(lèi)。上層類(lèi)加載器是引導(dǎo)類(lèi)加載器(啟動(dòng)類(lèi)加載器),加載底層類(lèi)庫(kù) 1.1.2.3應(yīng)用程序類(lèi)加載器Java實(shí)現(xiàn),派生于 ClassLoader 類(lèi)。上層類(lèi)加載器是擴(kuò)展類(lèi)加載器,加載自定義類(lèi)。 1.1.3 雙親委派機(jī)制加載類(lèi)時(shí),向上委派,交給最上層的加載器啟動(dòng)類(lèi)加載器加載核心類(lèi)庫(kù),加載不到就用擴(kuò)展類(lèi)加載器加載底層類(lèi)庫(kù),加載不到就用應(yīng)用程序類(lèi)加載器加載自定義類(lèi) 雙親委派機(jī)制的優(yōu)點(diǎn)
1.1.4沙箱安全機(jī)制作用是防止惡意代碼污染 Java 源代碼 如果一個(gè)類(lèi)在引導(dǎo)類(lèi)加載器那里就加載到了,先找到先使用,所以就使用引導(dǎo)類(lèi)加載器里面的類(lèi),后面的一概不能使用,這就保證了不被惡意代碼污染。 1.1.5 類(lèi)的主動(dòng)使用和被動(dòng)使用JVM 規(guī)定,每個(gè)類(lèi)或者接口被首次主動(dòng)使用時(shí)才對(duì)其進(jìn)行初始化,有主動(dòng)使用,自然就有被動(dòng)使用。
被動(dòng)使用:其實(shí)除了上面的幾種主動(dòng)使用其余就是被動(dòng)使用了 注意:主動(dòng)使用和被動(dòng)使用的區(qū)別在于類(lèi)是否會(huì)被初始化.
1.2 運(yùn)行時(shí)數(shù)據(jù)區(qū)JVM 的運(yùn)行時(shí)數(shù)據(jù)區(qū),不同虛擬機(jī)實(shí)現(xiàn)可能略微有所不同,但都會(huì)遵從 Java 虛擬機(jī)規(guī)范,Java 8 虛擬機(jī)規(guī)范規(guī)定,Java 虛擬機(jī)所管理的內(nèi)存將會(huì)包括程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧、Java堆、方法區(qū)。 1.2.1 程序計(jì)數(shù)器(Program Counter Register)JVM 中的程序計(jì)數(shù)寄存器中的 Register 命名源于CPU 的寄存器,寄存器存儲(chǔ)指令相關(guān)的現(xiàn)場(chǎng)信息。這里,并非是廣義上所指的物理寄存器,或許將其翻譯為 PC 計(jì)數(shù)器(或指令計(jì)數(shù)器)會(huì)更加貼切(也稱(chēng)為程序鉤子)。JVM 中的PC 寄存器是對(duì)物理 PC 寄存器的一種抽象模擬。
那么為什么使用程序計(jì)數(shù)器記錄當(dāng)前線程的執(zhí)行地址呢?我們都知道CPU需要不停的切換各個(gè)線程,這時(shí)候從其他線程切換到這個(gè)線程時(shí),根據(jù)程序計(jì)數(shù)器就知道該從哪里開(kāi)始執(zhí)行了。JVM 的字節(jié)碼解釋器就需要通過(guò)改變程序計(jì)數(shù)器的值來(lái)明確下一條應(yīng)該執(zhí)行什么樣的字節(jié)碼指令,由于這個(gè)原因,必須為每個(gè)線程分配一個(gè)程序計(jì)數(shù)器,這樣各個(gè)線程就可以互不干擾。 1.2.2 Java 虛擬機(jī)棧(Java Virtual Machine Stacks)首先我們需要學(xué)會(huì)區(qū)分棧和堆: 棧是運(yùn)行時(shí)的單位,而堆時(shí)存儲(chǔ)的單位。
1.2.2.1 Java虛擬機(jī)棧概述每個(gè)線程在創(chuàng)建時(shí)都會(huì)創(chuàng)建一個(gè)虛擬機(jī)棧,其內(nèi)部保存一個(gè)個(gè)棧幀,對(duì)應(yīng)著一次方法的調(diào)用。Java 虛擬機(jī)棧是線程私有的。生命周期和線程一致。棧中的數(shù)據(jù)都以棧幀為單位存儲(chǔ)。棧幀是一個(gè)內(nèi)存區(qū)塊,是一個(gè)數(shù)據(jù)集,維系著方法執(zhí)行過(guò)程中的各種數(shù)據(jù)信息。 作用:主要負(fù)責(zé)Java程序的運(yùn)行,保存方法內(nèi)的局部變量,還有部分結(jié)果,還參與方法的調(diào)用和返回。 棧是一種快速有效的分配存儲(chǔ)方式,訪問(wèn)速度僅次于程序計(jì)數(shù)器。JVM 直接對(duì) Java 棧的操作只有兩個(gè):
注意:對(duì)于棧來(lái)說(shuō)不存在垃圾回收問(wèn)題。 1.2.2.2棧的運(yùn)行原理
1.2.2.3 棧幀的內(nèi)部每個(gè)棧幀中都有:局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法返回地址、一下附加信息。 1.2.2.3.1局部變量表(Local Variables)局部變量表用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。 對(duì)于基本數(shù)據(jù)類(lèi)型的變量,則直接存儲(chǔ)它的值,對(duì)于引用類(lèi)型的變量,則存的是指向?qū)ο蟮囊谩?/p> 1.2.2.3.2 操作數(shù)棧(Operand Stack)(或表達(dá)式棧)程序中的所有計(jì)算過(guò)程都是在借助于操作數(shù)棧來(lái)完成的。 1.2.2.3.3 動(dòng)態(tài)鏈接(Dynamic Linking) (或指向運(yùn)行時(shí)常量池的方法引用)因?yàn)樵诜椒▓?zhí)行的過(guò)程中有可能需要用到類(lèi)中的常量或方法,所以必須要有一個(gè)引用指向運(yùn)行時(shí)常量池。 1.2.2.3.4 方法返回地址(Return Address)(或方法正常退出或者異常退出的定義)當(dāng)一個(gè)方法執(zhí)行完畢之后,要返回之前調(diào)用它的地方,因此在棧幀中必須保存一個(gè)方法返回地址。 1.2.2.3.5 一些附加信息例如和調(diào)試相關(guān)的信息,這部分信息完全取決于不同的虛擬機(jī)實(shí)現(xiàn)。 1.2.3 本地方法棧
內(nèi)存溢出方面也是相同的。
1.2.4 堆內(nèi)存堆內(nèi)存概述:
我們?cè)谥笤诩?xì)講堆內(nèi)存的區(qū)域劃分及垃圾回收機(jī)制 1.2.5方法區(qū)方法區(qū),是一個(gè)被線程共享的內(nèi)存區(qū)域。其中主要存儲(chǔ)加載的類(lèi)字節(jié)碼、class/method/field 等元數(shù)據(jù)、static final 常量、static 變量、編譯器編譯后的代碼等數(shù)據(jù)。另外,方法區(qū)包含了一個(gè)特殊的區(qū)域“運(yùn)行時(shí)常量池”。 結(jié)語(yǔ)這次就先寫(xiě)這些,文中如果存在不對(duì)的地方,歡迎各位讀者批評(píng)指正。我會(huì)在今后更新本地方法接口、執(zhí)行引擎和垃圾回收機(jī)制等相關(guān)內(nèi)容,如果大家感興趣,可以關(guān)注博主,我們一起交流學(xué)習(xí)。
|
|
來(lái)自: KookNut39 > 《待分類(lèi)》