午夜精品人妻久久久-成年美女很黄的网站-在线看片免费人成视久网app-国产精品美女无遮挡一区二区-91精品国产综合久久久久-国产的免费视频又猛又爽又刺激-在线看片免费人成视久网app-久久香蕉国产精品视频-av一区二区三区高清

這些不可不知的JVM知識,我都用思維導圖整理好了

小伙伴們不管是通過 APP還是其他招聘軟件獲得面試時,面試官總會安排一些常見面試題去提問 。小編再網(wǎng)上搜集了Java的一部分常見面試題,希望對Java小伙伴的面試有一定幫助,也希望各位多多支持,推薦更多小伙伴一起加入 APP
JVM是面試中必問的部分,本文通過思維導圖以面向面試的角度整理JVM中不可不知的知識 。
先上圖:
1、JVM基本概念
1.1、JVM是什么
JVM 的全稱是 「Java」,也就是我們耳熟能詳?shù)?Java 虛擬機 。
JVM具備著計算機的基本運算方式,它主要負責把 Java 程序生成的字節(jié)碼文件,解釋成具體系統(tǒng)平臺上的機器指令jvm內(nèi)存模型及垃圾收集策略解析,讓其在各個平臺運行 。
JVM是運行在操作系統(tǒng)上的 , 它與硬件沒有直接的交互 。
當然 , 嚴格來說JVM也是虛擬機規(guī)范,有很多不同的實現(xiàn),Sun/和中的默認Java虛擬機是虛擬機,是目前使用范圍最廣的Java虛擬機,一般講到的JVM默認指的就是虛擬機 。
1.2、Java程序運行過程
我們都知道 Java 源文件,通過編譯器 , 能夠生產(chǎn)相應的.Class 文件,也就是字節(jié)碼文件,而字節(jié)碼文件又通過 Java 虛擬機中的解釋器,編譯成特定機器上的機器碼。
也就是如下:
每一種平臺的解釋器是不同的,但是實現(xiàn)的虛擬機是相同的,這也就是 Java 為什么能夠跨平臺的原因了  , 當一個程序從開始運行,這時虛擬機就開始實例化了,多個程序啟動就會存在多個虛擬機實例 。程序退出或者關閉,則虛擬機實例消亡,多個虛擬機實例之間數(shù)據(jù)不能共享 。
1.3、JDK、JRE、JVM
JDK中包含JRE,也包括JDK , 而JRE也包括JDK 。
范圍關系:JDK>JRE>JVM 。
2、JVM內(nèi)存區(qū)域
Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域 。根據(jù)《Java虛擬機規(guī)范》的規(guī)定 , Java虛擬機所管理的內(nèi)存將會包括以下幾個運行時數(shù)據(jù)區(qū)域:
當然 , 實際上,為了更好的適應 CPU 性能提升,最大限度提升JVM 運行效率 , JDK中各個版本對JVM進行了一些迭代,示意圖如下:
JDK1.6、JDK1.7、JDK1.8 JVM 內(nèi)存模型主要有以下差異:
2.1、程序計數(shù)器
一塊較小的內(nèi)存空間, 是當前線程所執(zhí)行的字節(jié)碼的行號指示器,每條線程都要有一個獨立的程序計數(shù)器 , 這類內(nèi)存也稱為“線程私有”的內(nèi)存 。
正在執(zhí)行 java 方法的話,計數(shù)器記錄的是虛擬機字節(jié)碼指令的地址(當前指令的地址) 。如果還是方法,則為空 。
這個內(nèi)存區(qū)域是唯一一個在虛擬機中沒有規(guī)定任何情況的區(qū)域 。
2.2、Java虛擬機棧
與程序計數(shù)器一樣,Java虛擬機棧(JavaStack)也是線程私有的,它的生命周期與線程相同 。
虛擬機棧描述的是Java方法執(zhí)行的線程內(nèi)存模型:每個方法被執(zhí)行的時候,Java虛擬機都 會同步創(chuàng)建一個棧?。⊿tack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等信息 。每一個方法被調(diào)用直至執(zhí)行完畢的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程 。
局部變量表存放了編譯期可知的各種Java虛擬機基本數(shù)據(jù)類型、對象引用(類型,它并不等同于對象本身,可能是一個指向?qū)ο笃鹗贾返囊弥羔?,也可能是指向一個代表對象的句柄或者其他與此對象相關的位置)和 類型(指向了一條字節(jié)碼指令的地址) 。
2.3、本地方法棧
本地方法棧()與虛擬機棧所發(fā)揮的作用是非常相似的,其區(qū)別只是虛擬機棧為虛擬機執(zhí)行Java方法(也就是字節(jié)碼)服務 , 而本地方法棧則是為虛擬機使用到的本地() 方法服務 。
Hot-Spot虛擬機直接把本地方法棧和虛擬機棧合二為一 。
與虛擬機棧一樣,本地方法棧也會在棧深度溢出或者棧擴展失敗時分別拋出和異常 。
2.4、Java堆
對于Java應用程序來說 , Java堆(Java Heap)是虛擬機所管理的內(nèi)存中最大的一塊 。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域 , 在虛擬機啟動時創(chuàng)建 。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,Java 世界里“幾乎”所有的對象實例都在這里分配內(nèi)存 。
Java堆是垃圾收集器管理的內(nèi)存區(qū)域,因此一些資料中它也被稱作“GC堆” 。
從回收內(nèi)存的角度看,
需要注意的是這些區(qū)域劃分僅僅是一部分垃圾收集器的共同特性或者說設計風格而已,而非某個Java虛擬機具體實現(xiàn)的固有內(nèi)存布局,里面已經(jīng)出現(xiàn)了不采用分代設計的新垃圾收集器 。
Java堆既可以被實現(xiàn)成固定大小的,也可以是可擴展的,不過當前主流的Java虛擬機都是按照可擴展來實現(xiàn)的(通過參數(shù)-Xmx和-Xms設定) 。如果在Java堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時,Java虛擬機將會拋出異常 。
2.5、方法區(qū)(JDK1.8移除)
方法區(qū)( Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載 的類型信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù) 。
在JDK1.8以前,使用永久代來實現(xiàn)方法區(qū),所以某些場合也認為方法區(qū)和永久代是一個概念 。
在JDK 6的 時候開發(fā)團隊就有放棄永久代,逐步改為采用本地內(nèi)存( )來實現(xiàn)方法區(qū)的計劃了,到了JDK 7的,已經(jīng)把原本放在永久代的字符串常量池、靜態(tài)變量等移出 , 而到了 JDK 8 , 終于完全廢棄了永久代的概念,改用在本地內(nèi)存中實現(xiàn)的元空間(Meta- space)來代替 , 把JDK 7中永久代還剩余的內(nèi)容(主要是類型信息)全部移到元空間中 。
如果方法區(qū)無法滿足新的內(nèi)存分配需求時,將拋出異常 。
2.6、運行時常量池
運行時常量池(Pool)是方法區(qū)的一部分——在JDK1.8已經(jīng)被移到了元空間 。
運行時常量池相對于Class文件常量池的一個重要特征是具備動態(tài)性,Java語言并不要求常量一定只有編譯期才能產(chǎn)生,也就是說,并非預置入Class文件中常量池的內(nèi)容才能進入運行時常量池 , 運行期間也可以將新的常量放入池中 , 這種特性被開發(fā)人員利用得比較多的便是類的 ()方法 。
2.7、直接內(nèi)存
直接內(nèi)存( )并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分 。
顯然 , 本機直接內(nèi)存的分配不會受到Java堆大小的限制,但是 , 既然是內(nèi)存,則肯定還是會受到本機總內(nèi)存(包括物理內(nèi)存、SWAP分區(qū)或者分頁文件)大小以及處理器尋址空間的限制 。
元空間從虛擬機 Java 堆中轉(zhuǎn)移到本地內(nèi)存,默認情況下,元空間的大小僅受本地內(nèi)存的限制 。jdk1.8 以前版本的 class 和 JAR 包數(shù)據(jù)存儲在下面,大小是固定的,而且項目之間無法共用,公有的 class,所以比較容易出現(xiàn) OOM 異常 。
升級 JDK 1.8 后,元空間配置參數(shù),-XX:=512M XX:=1024M 。
3、JVM中的對象
上面已經(jīng)了解Java虛擬機的運行時數(shù)據(jù)區(qū)域,我們接下來更進一步了解這些虛擬機內(nèi)存中數(shù)據(jù)的其他細節(jié),譬如它們是如何創(chuàng)建、如何布局以及如何訪問的 。以最常用的虛擬機和最常用的內(nèi)存區(qū)域Java堆為例,了解一下虛擬機在Java堆中對象分配、布局和訪問的全過程 。
3.1、對象的創(chuàng)建
Java對象創(chuàng)建的大概過程如下:
內(nèi)存分配的兩種?式:選擇以上兩種?式中的哪?種 , 取決于 Java 堆內(nèi)存是否規(guī)整 。? Java 堆內(nèi)存是否規(guī)整,取決于 GC收集器的算法是”標記-清除” , 還是”標記-整理”(也稱作”標記-壓縮”),值得注意的是,復制算法內(nèi)存也是規(guī)整的 。
3.2、對象的內(nèi)存布局
在虛擬機里 , 對象在堆內(nèi)存中的存儲布局可以劃分為三個部分:對象頭()、實例數(shù)據(jù)( Data)和對齊填充() 。
虛擬機對象的對象頭部分包括兩類信息 。第一類是用于存儲對象自身的運行時數(shù)據(jù),如哈希碼()、GC分代年齡、鎖狀態(tài)標志、線程持有的鎖、偏向線程ID、偏向時間戳等,這部分數(shù)據(jù)的長度在32位和64位的虛擬機(未開啟壓縮指針)中分別為32個比特和64個比特 , 官方稱它為“Mark Word” 。
3.3、對象的訪問定位
建?對象就是為了使?對象,我們的Java程序通過棧上的數(shù)據(jù)來操作堆上的具體對象 。對象的訪問?式有虛擬機實現(xiàn)?定,?前主流的訪問?式有①使?句柄和②直接指針兩種:
4、GC垃圾回收
對于垃圾回收,主要考慮的就是完成三件事:
4.1、如何判斷對象需要回收?
4.1.1、引用計數(shù)法
引用計數(shù)法的算法:
客觀地說,引用計數(shù)算法( )雖然占用了一些額外的內(nèi)存空間來進行計數(shù) , 但它的原理簡單,判定效率也很高,在大多數(shù)情況下它都是一個不錯的算法 。也有一些比較著名的應用案例 , 例如微軟COM(Model)技術(shù)、使用 3的、語言以及在游戲腳本領域得到許多應用的中都使用了引用計數(shù)算法進行內(nèi)存管理 。
但是,在Java 領域,至少主流的Java虛擬機里面都沒有選用引用計數(shù)算法來管理內(nèi)存,主要原因是,這個看似簡單的算法有很多例外情況要考慮,例如在處理處理一些相互依賴、循環(huán)引用時非常復雜 。
4.1.2、可達性分析算法
當前主流的商用程序語言(Java、C#,上溯至前面提到的古老的Lisp)的內(nèi)存管理子系統(tǒng),都是通過可達性分析( )算法來判定對象是否存活的 。這個算法的基本思路就是通過一系列稱為“GC Roots”的根對象作為起始節(jié)點集,從這些節(jié)點開始 , 根據(jù)引用關系向下搜索,搜索過程所走過的路徑稱為“引用鏈”( Chain),如果某個對象到GC Roots間沒有任何引用鏈相連 ,  或者用圖論的話來說就是從GC Roots到這個對象不可達時,則證明此對象是不可能再被使用的 。
GC Roots 包括;
4.1.3、引用
無論是通過引用計數(shù)算法判斷對象的引用數(shù)量,還是通過可達性分析算法判斷對象是否引用鏈可達 , 判定對象是否存活都和“引用”離不開關系 。
Java的引用分為四種:強引用( Re-)、軟引用(Soft )、弱引用(Weak )和虛引用( ) 。
4.2、垃圾收集算法
4.2.1、標記-清除算法
最早出現(xiàn)也是最基礎的垃圾收集算法是“標記-清除”(Mark-Sweep)算法,
算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后,統(tǒng)一回收掉所有被標記的對象,也可以反過來,標記存活的對象 , 統(tǒng)一回
收所有未被標記的對象 。標記過程就是對象是否屬于垃圾的判定過程 。
后續(xù)的收集算法大多都是以標記-清除算法為基礎jvm內(nèi)存模型及垃圾收集策略解析,對其缺點進行改進而得到的 。
它的主要缺點有兩個:
程的執(zhí)行效率都隨對象數(shù)量增長而降低;
標記-清除算法的執(zhí)行過程如圖:
4.2.2、標記-復制算法
標記-復制算法常被簡稱為復制算法 。為了解決標記-清除算法面對大量可回收對象時執(zhí)行效率低的問題 。
它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊 。當這一塊的內(nèi)存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉 。
這樣實現(xiàn)簡單 , 運行高效,不過其缺陷也顯而易見,這種復制回收算法的代價是將可用內(nèi)存縮小為了原來的一半,空間浪費較多 。
標記-復制算法的執(zhí)行過程如圖所示 。
4.2.3、標記-整理算法
標記-整理算法的標記過程仍然與“標記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向內(nèi)存空間一端移動,然后直接清理掉邊界以外的內(nèi)存 。
“標記-整理”算法的示意圖如圖:
4.3、分代收集理論
當前商業(yè)虛擬機的垃圾收集器,大多數(shù)都遵循了“分代收集”( )的理論進行設計,分代收集名為理論 , 實質(zhì)是一套符合大多數(shù)程序運行實際情況的經(jīng)驗法則,它建立在兩個分代假說之上:
基于這兩個假說,收集器應該將Java堆劃分出不同的區(qū)域,然后將回收對象依據(jù)其年齡(年齡即對象熬過垃圾收集過程的次數(shù))分配到不同的區(qū)域之中存儲 。
設計者一般至少會把Java堆劃分為新生代(Young )和老年代(Old )兩個區(qū)域 。顧名思義 , 在新生代中,每次垃圾收集
時都發(fā)現(xiàn)有大批對象死去,而每次回收后存活的少量對象,將會逐步晉升到老年代中存放 。
基于這種分代 , 老年代和新生代具備不同的特點,可以采用不同的垃圾收集算法 。
因為有了分代收集理論,所以就有了了“Minor GC(新?代GC)”、“Major GC(?年代GC)”、“Full GC(全局GC)”這樣的回收類型的劃分
4.4、垃圾收集器
4.4.1、收集器
收集器是最基礎、歷史最悠久的收集器,曾經(jīng)(在JDK 1.3.1之前)是虛擬機新生代收集器的唯一選擇 。這個收集器是一個單線程工作的收集器,但它的“單線 程”的意義并不僅僅是說明它只會使用一個處理器或一條收集線程去完成垃圾收集工作,更重要的是強調(diào)在它進行垃圾收集時,必須暫停其他所有工作線程,直到它收集結(jié)束 。
/ Old收 集器的運行過程如下:
4.4.2、收集器
收集器實質(zhì)上是收集器的多線程并行版本,除了同時使用多條線程進行垃圾收集之外,其余的行為包括收集器可用的所有控制參數(shù)(例如:-XX:、-XX:ld、-XX:re等)、收集算法、Stop The World、對象分配規(guī)則、回收策略等都與收集器完全一致 , 在實現(xiàn)上這兩種收集器也共用了相當多的代碼 。
收集器的工作過程如圖所示:
4.4.3、 收集器
收集器也是一款新生代收集器,它同樣是基于標記-復制算法實現(xiàn)的收集器,也是能夠并行收集的多線程收集器
收集器的目標則是達到一個可控制的吞吐量() 。由于與吞吐量關系密切 ,  收集器也經(jīng)常被稱作“吞吐量優(yōu)先收集器” 。
收集器提供了兩個參數(shù)用于精確控制吞吐量,分別是控制最大垃圾收集停頓時間的-XX:參數(shù)以及直接設置吞吐量大小的-XX:參數(shù) 。
4.4.4、 Old收集器
Old是收集器的老年代版本,它同樣是一個單線程收集器 , 使用標記-整理算法 。這個收集器的主要意義也是供客戶端模式下的虛擬機使用 。如果在服務端模式下,它也可能有兩種用途:一種是在JDK 5以及之前的版本中與 收集器搭配使用,另外一種就是作為CMS 收集器發(fā)生失敗時的后備預案,在并發(fā)收集發(fā)生 Mode 時使用 。這兩點都將在后面的內(nèi)容中繼續(xù)講解 。
Old收集器的工作過程如圖所示 。
4.4.5、 Old收集器
Old是 收集器的老年代版本 , 支持多線程并發(fā)收集,基于標記-整理算法實現(xiàn) 。這個收集器是直到JDK 6時才開始提供的 。Old收集器的工作過程如圖所示 。
4.4.6、CMS收集器
CMS( Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器 。目前很大一部分的Java應用集中在互聯(lián)網(wǎng)網(wǎng)站或者基于瀏覽器的B/S系統(tǒng)的服務端上,這類應用通常都會較為服務的響應速度,希望系統(tǒng)停頓時間盡可能短,以給用戶帶來良好的交互體驗 。CMS收集器就非常符合這類應用的需求 。
Mark Sweep收集器運行過程如圖:
4.4.7、 First收集器
G1是一款主要面向服務端應用的垃圾收集器,是目前垃圾回收器的前沿成果 。開發(fā)團隊最初賦予它的期望是(在比較長期的)未來可以替換掉JDK 5中發(fā)布的CMS收集器 ?,F(xiàn)在這個期望目標已經(jīng)實現(xiàn)過半了 , JDK 9發(fā)布之日 , G1宣告取代 加 Old組合 , 成為服務端模式下的默認垃圾收集器 。
G1收集器運行過程如圖:
5、JVM類加載
Java虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進行校驗、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這個過程被稱作虛擬機的類加載機制 。
5.1、類加載過程
一個類型從被加載到虛擬機內(nèi)存中開始,到卸載出內(nèi)存為止,它的整個生命周期將會經(jīng)歷加載 ()、驗證()、準備()、解析()、初始化 ()、使用(Using)和卸載()七個階段,其中驗證、準備、解析三個部分統(tǒng)稱為連接() 。
過程如下圖:
加載 :
“加載”()階段是整個“類加載”(Class )過程中的一個階段,在加載階段,Java虛擬機需要完成以下三件事情:
驗證:
驗證是連接階段的第一步,這一階段的目的是確保Class文件的字節(jié)流中包含的信息符合《Java虛擬機規(guī)范》的全部約束要求,保證這些信息被當作代碼運行后不會危害虛擬機自身的安全 。
驗證階段大致上會完成四個階段的檢驗動作:文件格式驗證、元數(shù)據(jù)驗證、字節(jié)碼驗證和符號引用驗證 。
準備:
準備階段是正式為類中定義的變量(即靜態(tài)變量,被修飾的變量)分配內(nèi)存并設置類變量初始值的階段 。
解析:
解析階段是Java虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程 。
5.2、類加載器
【這些不可不知的JVM知識,我都用思維導圖整理好了】Java虛擬機設計團隊有意把類加載階段中的“通過一個類的全限定名來獲取描述該類的二進制字節(jié)流”這個動作放到Java虛擬機外部去實現(xiàn) , 以便讓應用程序自己決定如何去獲取所需的類 。實現(xiàn)這個動作的代碼被稱為“類加載器”(Class ) 。
5.2.1、類與類加載器
類加載器雖然只用于實現(xiàn)類的加載動作,但它在Java程序中起到的作用卻遠超類加載階段 。對于任意一個類,都必須由加載它的類加載器和這個類本身一起共同確立其在Java虛擬機中的唯一性,每一個類加載器,都擁有一個獨立的類名稱空間 。這句話可以表達得更通俗一些:比較兩個類是否“相等”,只有在這兩個類是由同一個類加載器加載的前提下才有意義 , 否則,即使這兩個類來源于同一個Class文件,被同一個Java虛擬機加載,只要加載它們的類加載器不同 , 那這兩個類就必定不相等 。
5.2.2、雙親委派模型
JVM 中內(nèi)置了三個重要的  , 啟動類加載器( ),這個類加載器使用C++語言實現(xiàn),是虛擬機自身的一部分,其他所有
的類加載器,這些類加載器都由Java語言實現(xiàn),獨立存在于虛擬機外部 , 并且全都繼承自抽象類java.lang. 。
雙親委派模型: 如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到最頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去完成加載 。
**為什么要使用雙親委派模型來組織類加載器之間的關系呢?**一個顯而易見的好處就是Java中的類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關系 。例如類java.lang.,它存放在rt.jar之中,無論哪一個類加載器要加載這個類,最終都是委派給處于模型最頂端的啟動類加載器進行加載,因此類在程序的各種類加載器環(huán)境中都能夠保證是同一個類 。反之,如果沒有使用雙親委派模型,都由各個類加載器自行去加載的話,如果用戶自己也編寫了一個名為java.lang.的類,并放在程序的 中 , 那系統(tǒng)中就會出現(xiàn)多個不同的類,Java類型體系中最基礎的行為也就無從保證,應用程序?qū)兊靡黄靵y 。
5.2.3、破壞雙親委派模型
過雙親委派模型并不是一個具有強制性約束的模型,而是Java設計者推薦給開發(fā)者們的類加載器實現(xiàn)方式 。在Java的世界中大部分的類加載器都遵循這個模型,但也有例外的情況 , 直到Java模塊化出現(xiàn)為止,雙親委派模型主要出現(xiàn)過3次較大規(guī)模“被破壞”的情況 。
JDK1.2發(fā)布之前 , 兼容之前的代碼 。
第二次被破壞是這個模型自身的缺陷導致的 。雙親委派模型很好的解決了各個類加載器的基礎類的統(tǒng)一問題(越基礎的類由越上層的加載器進行加載),基礎類之所以稱為“基礎”,是因為它們總是作為被用戶代碼調(diào)用的API,但沒有絕對,如果基礎類調(diào)用會用戶的代碼怎么辦呢?
這不是沒有可能的 。一個典型的例子就是JNDI服務 , JNDI現(xiàn)在已經(jīng)是Java的標準服務,它的代碼由啟動類加載器去加載(在JDK1.3時就放進去的rt.jar),但它需要調(diào)用由獨立廠商實現(xiàn)并部署在應用程序的下的JNDI接口提供者(SPI,)的代碼,但啟動類加載器不可能“認識“這些代碼啊 。因為這些類不在rt.jar中,但是啟動類加載器又需要加載 。怎么辦呢?
為了解決這個問題,Java設計團隊只好引入了一個不太優(yōu)雅的設計:線程上下文類加載器() 。這個類加載器可以通過java.lang.類的r方法進行設置 。如果創(chuàng)建線程時還未設置,它將會從父線程中繼承一個 , 如果在應用程序的全局范圍內(nèi)都沒有設置過多的話,那這個類加載器默認即使應用程序類加載器 。
有了線程上下文加載器 , JNDI服務使用這個線程上下文加載器去加載所需要的SPI代碼,也就是父類加載器請求子類加載器去完成類加載的動作,這種行為實際上就是打通了雙親委派模型的層次結(jié)構(gòu)來逆向使用類加載器,實際上已經(jīng)違背了雙親委派模型的一般性原則 。但這無可奈何 , Java中所有涉及SPI的加載動作基本勝都采用這種方式 。例如JNDI,JDBC,JCE,JAXB,JBI等 。
雙親委派模型的第三次“被破壞”是由于用戶對程序的動態(tài)性的追求導致的 。為了實現(xiàn)熱插拔,熱部署,模塊化,意思是添加一個功能或減去一個功能不用重啟,只需要把這模塊連同類加載器一起換掉就實現(xiàn)了代碼的熱替換 。例如OSGi的出現(xiàn) 。在OSGi環(huán)境下,類加載器不再是雙親委派模型中的樹狀結(jié)構(gòu) , 而是進一步發(fā)展為網(wǎng)狀結(jié)構(gòu) 。
如果我們自己想定義一個類加載器 , 破壞雙親委派模型,只需要重寫重寫其中的方法,使其不進行雙親委派即可 。
5.2.4、類加載器架構(gòu)
是主流的Java Web服務器之一 , 為了實現(xiàn)一些特殊的功能需求,自定義了一些類加載器 。
類加載器如下:
實際上也是破壞了雙親委派模型的 。
是web容器,可能需要部署多個應用程序 。不同的應用程序可能會依賴同一個第三方類庫的不同版本,但是不同版本的類庫中某一個類的全路徑名可能是一樣的 。如多個應用都要依賴.jar,但是A應用需要依賴1.0.0版本,但是B應用需要依賴1.0.1版本 。這兩個版本中都有一個類是com..Test.class 。如果采用默認的雙親委派類加載機制,那么無法加載多個相同的類 。
所以,破壞雙親委派原則,提供隔離的機制,為每個web容器單獨提供一個加載器 。
的類加載機制:為了實現(xiàn)隔離性,優(yōu)先加載 Web 應用自己定義的類,所以沒有遵照雙親委派的約定 , 每一個應用自己的類加載器——負責加載本身的目錄下的class文件,加載不到時再交給加載,這和雙親委派剛好相反 。
6、JVM故障處理
6.1、基礎故障處理工具
6.1.1、jps:虛擬機進程狀況工具
jps(JVMTool),它的功能與 ps 命令類似 , 可以列出正在運行的虛擬機進程,并顯示虛擬機執(zhí)行主類(Main Class,main()函數(shù)所在的類)
名稱以及這些進程的本地虛擬機唯一 ID ( Local,LVMID) , 類似于 ps -ef | grep java 的功能 。
命令格式
jps [] []
6.1.2、jstat:虛擬機統(tǒng)計信息監(jiān)視工具
jstat(JVMTool),用于監(jiān)視虛擬機各種運行狀態(tài)信息 。它可以查看本地或者遠程虛擬機進程中,類加載、內(nèi)存、垃圾收集、即時編譯等運行時數(shù)據(jù) 。
命令格式
jstat -[-t] [-h] [ []]
[:][//]lvmid[@[:port]/]
選項列表:
6.1.3、jinfo:Java配置信息工具
jinfo( Info for Java),實時查看和調(diào)整 JVM 的各項參數(shù) 。在上面講到 jps -v 指令時 , 可以看到它把虛擬機啟動時顯式的參數(shù)列表都打印
出來了,但如果想更加清晰的看具體的一個參數(shù)或者想知道未被顯式指定的參數(shù)時,就可以通過 jinfo -flag 來查詢了 。
命令格式
jinfo [] pid
6.1.4、jmap:Java內(nèi)存映像工具
jmap( Map for Java) , 用于生成堆轉(zhuǎn)儲快照( 文件) 。
jmap 的作用除了獲取堆轉(zhuǎn)儲快照 , 還可以查詢執(zhí)行隊列、Java 堆和
方法區(qū)的詳細信息 。
命令格式
jmap [] pid
6.1.5、jhat:虛擬機堆轉(zhuǎn)儲快照分析工具
jhat(JVM HeapTool),與 jmap 配合使用,用于分析 jmap 生成的堆轉(zhuǎn)儲快照 。
jhat 內(nèi)置了一個小型的 http/web 服務器,可以把堆轉(zhuǎn)儲快照分析的結(jié)果 , 展示在瀏覽器中查看 。不過用途不大,基本大家都會使用其他第三方工具 。
命令格式
jhat [-stack ] [-refs ] [-port ] [- ] [-
debug ] [-] [-h|-help]
6.1.6、:Java堆棧跟蹤工具
(Stack Trace for Java),用于生成虛擬機當前時刻的線程快照(、) 。
線程快照就是當前虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的目的通常是定位線程出現(xiàn)長時間停頓的原因,如:線程死鎖、死循環(huán)、請求
外部資源耗時較長導致掛起等 。
線程出現(xiàn)聽頓時通過來查看各個線程的調(diào)用堆棧,就可以獲得沒有響應的線程在搞什么鬼 。
命令格式
[] vmid
選項參數(shù):
6.2、可視化故障處理工具
JDK中除了附帶大量的命令行工具外,還提供了幾個功能集成度更高的可視化工具,用戶可以使用這些可視化工具以更加便捷的方式進行進程故障診斷和調(diào)試工作 。這類工具主要包括、 JHSDB、和JMC四個 。
6.2.1、JHSDB:基于服務性代理的調(diào)試工具
JDK中提供了JCMD和JHSDB兩個集成式的多功能工具箱,它們不僅整合了所有 基礎工具所能提供的專項功能 , 而且由于有著“后發(fā)優(yōu)勢”,能夠做得往往比之前的老工具們更好、更強大 。
JHSDB是一款基于服務性代理( Agent,SA)實現(xiàn)的進程外調(diào)試工具 。
使用以下命令進入JHSDB的圖形化模式,并使其附加進程11180:
jhsdb hsdb --pid 11180
命令打開的JHSDB的界面:
6.2.2、:Java監(jiān)視與管理控制臺
(Javaand)是一款基于JMX(Java -ment )的可視化監(jiān)視、管理工具 。它的主要功能是通過JMX的MBean( Bean)對系統(tǒng)進 行信息收集和參數(shù)動態(tài)調(diào)整 。
連接頁面 :
通過JDK/bin目錄下的.exe啟動JCon-sole后 , 會自動搜索出本機運行的所有虛擬機進程
6.2.3、:多合-故障處理工具
(All-in-One JavaTool)是功能最強大的運行監(jiān)視和故障處理程序之一,曾經(jīng)在很長一段時間內(nèi)是官方主力發(fā)展的虛擬機故障處理工具 。
它除了常規(guī)的運行監(jiān)視、故障處理外,還可以做性能分析等工作 。因為它的通用性很強,對應用程序影響較小,所以可以直接接入到生產(chǎn)環(huán)境中 。
的插件可以手工進行安裝,在網(wǎng)站上下載nbm包后,點擊“工具->插件->已下載”菜單,然后在彈出對話框中指定nbm包路徑便可完成安裝 。
插件頁簽:
6.2.4、Java:可持續(xù)在線的監(jiān)控工具
JMC最初是BEA公司的產(chǎn)品,因此并沒有像那樣一開始就基于自家的Net-Beans平臺來開發(fā) , 而是選擇了由IBM捐贈的 RCP作為基礎框架 , 現(xiàn)在的JMC不僅可以下載到獨立程序,更常見的是作為的插件來使用 。JMC與虛擬機之間同樣采取JMX協(xié)議進行通信,JMC一方面作為 JMX控制臺 , 顯示來自虛擬機MBean提供的數(shù)據(jù);另一方面作為JFR的分析工具,展示來自JFR的數(shù)據(jù) 。
JMC的主界面如圖:
本文到此結(jié)束,希望對大家有所幫助 。