大良做網(wǎng)站網(wǎng)頁制作作業(yè)100例
?
目錄
?
?什么是JVM
??JVM的運行流程
??JVM運行時數(shù)據(jù)區(qū)
?虛擬機棧
?本地方法棧
?堆
?程序計數(shù)器
?方法區(qū)/元數(shù)據(jù)區(qū)
?
?類加載的過程
?
?雙親委派模型
???垃圾回收機制
?什么是JVM
JVM 是 Java Virtual Machine 的簡稱,意為 Java虛擬機。 虛擬機是指通過軟件模擬的具有完整硬件功能的、運行在一個完全隔離的環(huán)境中的完整計算機系統(tǒng)(如:JVM、VMwave、Virtual Box)。 ?JVM 和其他兩個虛擬機的區(qū)別是: VMwave與VirtualBox是通過軟件模擬物理CPU的指令集,物理系統(tǒng)中會有很多的寄存器,而 JVM則是通過軟件模擬Java字節(jié)碼的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都進行了裁剪。?
??JVM的運行流程
JVM 是 Java 運行的基礎,也是實現(xiàn)一次編譯到處執(zhí)行的關(guān)鍵,那么 JVM 是如何執(zhí)行的呢?
我們知道程序在執(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個主要組成部分的職責與功能。
??JVM運行時數(shù)據(jù)區(qū)
從上圖我們可以發(fā)現(xiàn)運行時數(shù)據(jù)區(qū)劃分成5個部分,接下來我們就來看看他的內(nèi)存布局。
?虛擬機棧
虛擬機棧是給 Java 代碼使用的棧,每個線程都會有一個,虛擬機棧描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息:
①. 局部變量表: 存放了編譯器可知的各種基本數(shù)據(jù)類型(8大基本數(shù)據(jù)類型)、對象引用。局部變量表 所需的內(nèi)存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在執(zhí)行期間不會改變局部變量表大小。簡單來說就是存放方法參數(shù)和局部變量。
②. 操作棧:每個方法會生成一個先進后出的操作棧。
③. 動態(tài)鏈接:指向運行時常量池的方法引用。
④. 方法返回地址:PC 寄存器的地址。
?本地方法棧
Native Method Stack 中 Native 就表示?JVM 內(nèi)部 C++ 寫的代碼,就是給調(diào)用 Native 方法( JVM 內(nèi)部的方法)準備的棧空間。
?堆
整個 JVM 中最大的區(qū)域,所有 new 出來的對象(類的普通成員變量)都是在堆上。
?程序計數(shù)器
程序計數(shù)器是一塊比較小的內(nèi)存空間,記錄當前線程執(zhí)行到哪個指令,可以看做是當前線程所執(zhí)行的字節(jié)碼的行號指示器。 如果當前線程正在執(zhí)行的是一個 Java 方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是一個 Native 方法,這個計數(shù)器值為空。
?方法區(qū)/元數(shù)據(jù)區(qū)
元數(shù)據(jù)區(qū)是用來存儲被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)的。
注:虛擬機棧、本地方法棧、程序計數(shù)器都是線程私有的(一個線程有一個),堆、元數(shù)據(jù)區(qū)是線程公有的(一個進程里的所有線程共用一個)。
?
?類加載的過程
Java類加載是將 .class 文件中的二進制數(shù)據(jù)讀入到內(nèi)存中,并對數(shù)據(jù)進行校驗、解析和初始化的過程。類加載來說總共分為以下幾個步驟:
?.?加載:把 .class 文件找到,讀取文件內(nèi)容
?.?連接:
????????①. 驗證:根據(jù) JVM 規(guī)范,檢查 .class 文件是否符合規(guī)范
????????②. 準備:給類對象分配內(nèi)存空間,設置初始值(基本數(shù)據(jù)類型設為為 0,引用數(shù)據(jù)類型設為 null)
????????③. 解析:將常量池內(nèi)的符號引用替換為直接引用的過程,也就是初始化常量的過程。(字符串常量有一塊內(nèi)存空間存字符串的實際內(nèi)容,還有一個引用保存這塊空間的起始地址,類加載前字符串常量存儲在 .class 文件中(還沒有內(nèi)存地址),此時這個引用記錄的是字符串常量在文件中的”偏移量“(符號引用),在類加載后字符串常量才放到內(nèi)存里(有了內(nèi)存地址),才會將”偏移量“替換成內(nèi)存地址(直接引用))
?. 初始化:
真正對類對象里的內(nèi)容進行加載,加載父類、執(zhí)行靜態(tài)代碼塊
注:java 程序運行后不會把所有類一次性都加載,而是需要用到哪個再加載哪個
?
?雙親委派模型
類加載描述的是找到 .class 文件讀取內(nèi)容的過程,而雙親委派模型描述的就是加載、找 .class文件的基本過程。了解雙親委派模型前,我們得先了解下 JVM 默認提供的三種類加載器:?.?BootstrapClassLoader:負責加載標準庫中的類(java 規(guī)范要求提供的類,無論哪種 JVM 都會提供)
?.?ExtensionClassLoader:負責加載 JVM 擴展庫中的類(規(guī)范之外,由 JVM 廠商提供的擴展功能)
?.?ApplicationClassLoader:負責加載用戶提供的第三方庫/用戶項目代碼中的類?
這三個加載器彼此存在“父子類”的關(guān)系, BootstrapClassLoader?相當于?ExtensionClassLoder?的父加載器,ExtensionClassLoder?相當于?ApplicationClassLoder的父加載器。
雙親委派模型就是單加載一個類時,首先從 ApplicationClassLoader 開始,但?ApplicationClassLoader 會把加載任務交給父加載器?ExtensionClassLoader , ExtensionClassLoader 又會把加載任務交給父加載器?BootstrapClassLoader ,BootstrapClassLoader 沒有父加載器才開始搜索標準庫目錄的類,找到了就加載,沒找到就交給子加載器?ExtensionClassLoader,ExtensionClassLoader 搜索擴展庫的目錄,找到了就加載,沒找到就交給子加載器 ApplicationClassLoader,ApplicationClassLoader 搜索用戶項目相關(guān)目錄,找到了就加載,沒找到就拋出異常。
注:雙親委派模型的加載順序確保了 BootstrapClassLoader 先加載,ApplicationClassLoader 后加載,可以避免因用戶自己寫的類導致 JVM 已有代碼的混亂。
???垃圾回收機制
垃圾回收機制即 GC 主要是對堆進行釋放的,是以對象為單位進行回收的,因此也叫死亡對象的回收。
要想回收垃圾,首先得判斷誰是垃圾,常見的判斷是否為垃圾的方法有兩種:
?.引用計數(shù)
給每個對象都分配一個計數(shù)器,有引用指向它,計數(shù)器加一;有指向它的引用銷毀,計數(shù)器減一。
顯然這個方法簡單有效,但還是存在缺點:內(nèi)存浪費的多且可能存在循環(huán)引用(a 對象的屬性指向 b,b 對象的屬性指向 a,當 a 和 b 銷毀時,a 和 b 的引用計數(shù)仍為 1)的問題。
?.可達性分析
java 里的對象都是通過引用指向來訪問的,通過遍歷所有對象的引用指向就可以判斷出某個對象可達不可達,java 的做法就是通過可達性分析。
確認了哪個對象是垃圾就可以對垃圾進行回收,常見的回收垃圾的做法有:
?.標記清除
基本概念:標記清除算法將垃圾回收分為兩個階段:標記階段和清除階段。在標記階段,它從根節(jié)點開始遍歷,標記所有可達的對象。未被標記的對象被視為垃圾,這些對象在清除階段被回收。
優(yōu)點:標記清除算法實現(xiàn)簡單,不需要移動存活對象。
缺點:標記清除算法執(zhí)行效率較低,且清除后容易產(chǎn)生大量不連續(xù)的內(nèi)存碎片,這可能導致后續(xù)對象分配時找不到足夠的連續(xù)內(nèi)存空間而提前觸發(fā)垃圾回收。?.復制算法
基本概念:復制算法將內(nèi)存分為兩塊,每次只使用其中一塊。當一塊內(nèi)存用完時,將還存活的對象復制到另一塊上,然后清理掉已使用的內(nèi)存。
優(yōu)點:由于只處理其中一塊內(nèi)存區(qū)域,復制算法運行速度較快,且不會產(chǎn)生內(nèi)存碎片。
缺點:復制算法需要兩倍的內(nèi)存空間,代價較高。同時,如果對象的生命周期較長,這種復制操作會導致效率低下。
?.標記整理
基本概念:標記整理算法結(jié)合了標記清除和復制算法的優(yōu)點。在標記階段后,將所有存活對象壓縮到內(nèi)存的一端,然后清理邊界以外的內(nèi)存。
優(yōu)點:標記整理算法避免了標記清除算法的碎片問題,也不需要復制算法那么多的內(nèi)存空間。
缺點:標記整理算法實現(xiàn)較為復雜,且移動對象的過程會產(chǎn)生額外的開銷。?.分代回收
基本概念:分代收集算法基于這樣一個事實:大部分對象會在年輕時死亡。它將堆內(nèi)存分為新生代和老年代,不同年代采用不同的回收算法。新 new 出來的對象在伊甸區(qū),熬過一輪 GC 就通過復制算法來到了幸存區(qū),幸存也要經(jīng)過周期性的 GC 考驗,如果通過考驗就進入另一個幸存區(qū),沒通過就釋放掉,當一個對象在兩個幸存區(qū)來回拷貝很多次了后就進入老年區(qū),老年區(qū)偶而也要經(jīng)歷 GC 的考驗,如果沒通過就通過標記整理算法釋放掉。
優(yōu)點:分代收集算法通過將內(nèi)存分區(qū),可以更高效地回收垃圾,特別是針對新生代中大量短命的對象。
缺點:分代收集算法設計相對復雜,需要根據(jù)不同代的特點選擇合適的回收算法。注:JVM 就是基于分代回收算法回收垃圾的。