2020重新出發(fā)承疲,JAVA高級邻耕,JVM

JVM的基本概念

JVM是可運(yùn)行java代碼的假想計(jì)算機(jī),包括一套字節(jié)碼指令集燕鸽、一組寄存器兄世、一個(gè)棧、一個(gè)垃圾回收啊研,堆和一個(gè)存儲方法域御滩。JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒有直接的交互党远。

在這里插入圖片描述

JVM的運(yùn)行過程

我們都知道Java源文件削解,通過編譯器,能夠生產(chǎn)相應(yīng)的 .Class 文件沟娱,也就是字節(jié)碼文件氛驮,而字節(jié)碼文件又通過 Java 虛擬機(jī)中的解釋器,編譯成待定機(jī)器上的機(jī)器碼济似。

如下:

① Java源文件 —->編譯器 —->字節(jié)碼文件
② 字節(jié)碼文件 —->JVM—->機(jī)器碼

每一種平臺的解釋器是不同的矫废,但是實(shí)現(xiàn)的虛擬機(jī)是相同的,這也就是 Java 為什么能夠跨平臺的原因了 砰蠢,當(dāng)一個(gè)程序從開始運(yùn)行磷脯,這時(shí)虛擬機(jī)就開始實(shí)例化了,多個(gè)程序啟動(dòng)就會存在多個(gè)虛擬機(jī)實(shí)例娩脾。程序退出或者關(guān)閉赵誓,則虛擬機(jī)實(shí)例消亡,多個(gè)虛擬機(jī)實(shí)例之間數(shù)據(jù)不能共享柿赊。

在這里插入圖片描述

線程

這里所說的線程指程序執(zhí)行過程中的一個(gè)線程實(shí)體俩功。 JVM 允許一個(gè)應(yīng)用并發(fā)執(zhí)行多個(gè)線程。Hotspot JVM 中的 Java 線程與原生操作系統(tǒng)線程有直接的映射關(guān)系碰声。當(dāng)線程本地存儲诡蜓、緩沖區(qū)分配、同步對象胰挑、棧蔓罚、程序計(jì)數(shù)器等準(zhǔn)備好以后,就會創(chuàng)建一個(gè)操作系統(tǒng)原生線程瞻颂。Java 線程結(jié)束豺谈,原生線程隨之被回收。操作系統(tǒng)負(fù)責(zé)調(diào)度所有線程贡这,并把它們分配到任何可用的 CPU 上茬末。當(dāng)原生線程初始化完畢,就會調(diào)用 Java 線程的 run() 方法。當(dāng)線程結(jié)束時(shí)丽惭,會釋放原生線程和 Java 線程的所有資源击奶。

Hotspot JVM 后臺運(yùn)行的系統(tǒng)線程主要有下面幾個(gè):

  • 虛擬機(jī)線程(VM thread):這個(gè)線程等待 JVM 到達(dá)安全點(diǎn)操作出現(xiàn)。這些操作必須要在獨(dú)立的線程里執(zhí)行责掏,因?yàn)楫?dāng)堆修改無法進(jìn)行時(shí)柜砾,線程都需要 JVM 位于安全點(diǎn)。這些操作的類型有:stop-the-world 垃圾回收换衬、線程棧dump痰驱、線程暫停、線程偏向鎖(biased locking)解除冗疮。

  • 周期性任務(wù)線程 :這線程負(fù)責(zé)定時(shí)器事件(也就是中斷)萄唇,用來調(diào)度周期性操作的執(zhí)行檩帐。

  • GC 線程 :這些線程支持 JVM 中不同的垃圾回收活動(dòng)术幔。

  • 編譯器線程 :這些線程在運(yùn)行時(shí)將字節(jié)碼動(dòng)態(tài)編譯成本地平臺相關(guān)的機(jī)器碼。

  • 信號分發(fā)線程 這個(gè)線程接收發(fā)送到 JVM 的信號并調(diào)用適當(dāng)?shù)?JVM 方法處理湃密。

JVM內(nèi)存區(qū)域

在這里插入圖片描述

JVM 內(nèi)存區(qū)域主要分為線程私有區(qū)域【程序計(jì)數(shù)器诅挑、虛擬機(jī)棧、本地方法區(qū)】泛源、線程共享區(qū)域【JAVA堆拔妥、方法區(qū)】、直接內(nèi)存达箍。

線程私有數(shù)據(jù)區(qū)域生命周期與線程相同, 依賴用戶線程的啟動(dòng)/結(jié)束 而 創(chuàng)建/銷毀(在Hotspot VM內(nèi), 每個(gè)線程都與操作系統(tǒng)的本地線程直接映射, 因此這部分內(nèi)存區(qū)域的存/否跟隨本地線程的生/死對應(yīng))没龙。

線程共享區(qū)域隨虛擬機(jī)的啟動(dòng)/關(guān)閉而創(chuàng)建/銷毀。

直接內(nèi)存并不是JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分, 但也會被頻繁的使用: 在JDK 1.4引入的NIO提供了基于Channel與Buffer的IO方式, 它可以使用Native函數(shù)庫直接分配堆外內(nèi)存, 然后使用DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作(詳見: Java I/O 擴(kuò)展), 這樣就避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù), 因此在一些場景中可以顯著提高性能缎玫。

在這里插入圖片描述

程序計(jì)數(shù)器(線程私有)

一塊較小的內(nèi)存空間, 是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器硬纤,每條線程都要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,這類內(nèi)存也稱為“線程私有”的內(nèi)存赃磨。

正在執(zhí)行java方法的話筝家,計(jì)數(shù)器記錄的是虛擬機(jī)字節(jié)碼指令的地址(當(dāng)前指令的地址)。如果還是Native方法邻辉,則為空溪王。

這個(gè)內(nèi)存區(qū)域是唯一一個(gè)在虛擬機(jī)中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

虛擬機(jī)棧(線程私有)

是描述java方法執(zhí)行的內(nèi)存模型值骇,每個(gè)方法在執(zhí)行的同時(shí)都會創(chuàng)建一個(gè)棧幀(Stack Frame)
用于存儲局部變量表莹菱、操作數(shù)棧、動(dòng)態(tài)鏈接吱瘩、方法出口等信息芒珠。每一個(gè)方法從調(diào)用直至執(zhí)行完成
的過程,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程搅裙。

棧幀( Frame)是用來存儲數(shù)據(jù)和部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu)皱卓,同時(shí)也被用來處理動(dòng)態(tài)鏈接
(Dynamic Linking)裹芝、 方法返回值和異常分派( Dispatch Exception)。棧幀隨著方法調(diào)用而創(chuàng)

建娜汁,隨著方法結(jié)束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內(nèi)未被捕獲的異
常)都算作方法結(jié)束嫂易。

在這里插入圖片描述

本地方法區(qū)(線程私有)

本地方法區(qū)和Java Stack作用類似, 區(qū)別是虛擬機(jī)棧為執(zhí)行Java方法服務(wù), 而本地方法棧則為Native方法服務(wù), 如果一個(gè)VM實(shí)現(xiàn)使用C-linkage模型來支持Native調(diào)用, 那么該棧將會是一個(gè)C棧,但HotSpot VM直接就把本地方法棧和虛擬機(jī)棧合二為一掐禁。

堆(Heap-線程共享)-運(yùn)行時(shí)數(shù)據(jù)區(qū)

是被線程共享的一塊內(nèi)存區(qū)域怜械,創(chuàng)建的對象和數(shù)組都保存在 Java 堆內(nèi)存中,也是垃圾收集器進(jìn)行垃圾收集的最重要的內(nèi)存區(qū)域傅事。由于現(xiàn)代VM采用分代收集算法, 因此Java堆從GC的角度還可以細(xì)分為: 新生代(Eden區(qū)缕允、From Survivor區(qū)和To Survivor區(qū))和老年代

方法區(qū)/永久代(線程共享)

即我們常說的永久代(Permanent Generation), 用于存儲被JVM加載的類信息蹭越、常量障本、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù). HotSpot VM把GC分代收集擴(kuò)展至方法區(qū), 即使用Java堆的永久代來實(shí)現(xiàn)方法區(qū), 這樣HotSpot的垃圾收集器就可以像管理Java堆一樣管理這部分內(nèi)存, 而不必為方法區(qū)開發(fā)專門的內(nèi)存管理器(永久帶的內(nèi)存回收的主要目標(biāo)是針對常量池的回收和類型的卸載, 因此收益一般很小)响鹃。

運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分驾霜。Class文件中除了有類的版本、字段买置、方法粪糙、接口等描述等信息外,還有一項(xiàng)信息是常量池

(Constant Pool Table)忿项,用于存放編譯期生成的各種字面量和符號引用蓉冈,這部分內(nèi)容將在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中。 Java虛擬機(jī)對Class文件的每一部分(自然也包括常量池)的格式都有嚴(yán)格的規(guī)定轩触,每一個(gè)字節(jié)用于存儲哪種數(shù)據(jù)都必須符合規(guī)范上的要求寞酿,這樣才會被虛擬機(jī)認(rèn)可滑黔、裝載和執(zhí)行茅糜。

JVM運(yùn)行時(shí)內(nèi)存

Java堆從GC的角度還可以細(xì)分為: 新生代(Eden區(qū)、From Survivor區(qū)和To Survivor區(qū))和老年代机错。

在這里插入圖片描述

新生代

是用來存放新生的對象褐捻。一般占據(jù)堆的1/3空間掸茅。由于頻繁創(chuàng)建對象,所以新生代會頻繁觸發(fā)MinorGC進(jìn)行垃圾收柠逞。新生代又分為 Eden區(qū)昧狮、ServivorFrom、ServivorTo三個(gè)區(qū)板壮。

  1. Eden區(qū) :Java新對象的出生地(如果新創(chuàng)建的對象占用內(nèi)存很大逗鸣,則直接分配到老年代)。當(dāng)Eden區(qū)內(nèi)存不夠的時(shí)候就會觸發(fā)MinorGC,對新生代區(qū)進(jìn)行一次垃圾回收撒璧。
  2. ServivorFrom :上一次GC的幸存者透葛,作為這一次GC的被掃描者。
  3. ServivorTo :保留了一次MinorGC過程中的幸存者卿樱。

MinorGC的過程(復(fù)制->清空->互換)

MinorGC采用復(fù)制算法僚害。

  1. eden、servicorFrom 復(fù)制到ServicorTo繁调,年齡+1 :首先萨蚕,把Eden和ServivorFrom區(qū)域中存活的對象復(fù)制到ServicorTo區(qū)域(如果有對象的年齡以及達(dá)到了老年的標(biāo)準(zhǔn),則賦值到老年代區(qū))蹄胰,同時(shí)把這些對象的年齡+1(如果ServicorTo不夠位置了就放到老年區(qū))岳遥;

  2. 清空eden、servicorFrom :然后裕寨,清空Eden和ServicorFrom中的對象浩蓉;

  3. ServicorTo和ServicorFrom互換 :最后,ServicorTo和ServicorFrom互換帮坚,原ServicorTo成為下一次GC時(shí)的ServicorFrom區(qū)妻往。

老年代

主要存放應(yīng)用程序中生命周期長的內(nèi)存對象互艾。

老年代的對象比較穩(wěn)定试和,所以MajorGC不會頻繁執(zhí)行。在進(jìn)行MajorGC前一般都先進(jìn)行了一次MinorGC纫普,使得有新生代的對象晉身入老年代阅悍,導(dǎo)致空間不夠用時(shí)才觸發(fā)。當(dāng)無法找到足夠大的連續(xù)空間分配給新創(chuàng)建的較大對象時(shí)也會提前觸發(fā)一次MajorGC進(jìn)行垃圾回收騰出空間昨稼。

MajorGC采用標(biāo)記清除算法:首先掃描一次所有老年代节视,標(biāo)記出存活的對象,然后回收沒有標(biāo)記的對象假栓。MajorGC的耗時(shí)比較長寻行,因?yàn)橐獟呙柙倩厥铡ajorGC會產(chǎn)生內(nèi)存碎片匾荆,為了減少內(nèi)存損耗拌蜘,我們一般需要進(jìn)行合并或者標(biāo)記出來方便下次直接分配。當(dāng)老年代也滿了裝不下的時(shí)候牙丽,就會拋出OOM(Out of Memory)異常简卧。

永久代

指內(nèi)存的永久保存區(qū)域,主要存放Class和Meta(元數(shù)據(jù))的信息,Class在被加載的時(shí)候被放入永久區(qū)域烤芦,它和和存放實(shí)例的區(qū)域不同,GC不會在主程序運(yùn)行期對永久區(qū)域進(jìn)行清理举娩。所以這也導(dǎo)致了永久代的區(qū)域會隨著加載的Class的增多而脹滿,最終拋出OOM異常。

JAVA8與元數(shù)據(jù)

在Java8中铜涉,永久代已經(jīng)被移除智玻,被一個(gè)稱為“元數(shù)據(jù)區(qū)”(元空間)的區(qū)域所取代。元空間的本質(zhì)和永久代類似芙代,元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中尚困,而是使用本地內(nèi)存。因此链蕊,默認(rèn)情況下事甜,元空間的大小僅受本地內(nèi)存限制。類的元數(shù)據(jù)放入 native memory, 字符串池和類的靜態(tài)變量放入java堆中滔韵,這樣可以加載多少類的元數(shù)據(jù)就不再由MaxPermSize控制, 而由系統(tǒng)的實(shí)際可用空間來控制逻谦。

垃圾回收與算法

在這里插入圖片描述

如何確定垃圾

引用計(jì)數(shù)法

在Java中,引用和對象是有關(guān)聯(lián)的陪蜻。如果要操作對象則必須用引用進(jìn)行邦马。因此,很顯然一個(gè)簡單的辦法是通過引用計(jì)數(shù)來判斷一個(gè)對象是否可以回收宴卖。簡單說滋将,即一個(gè)對象如果沒有任何與之關(guān)聯(lián)的引用,即他們的引用計(jì)數(shù)都不為0症昏,則說明對象不太可能再被用到随闽,那么這個(gè)對象就是可回收對象

可達(dá)性分析

為了解決引用計(jì)數(shù)法的循環(huán)引用問題肝谭,Java使用了可達(dá)性分析的方法掘宪。通過一系列的“GC roots”對象作為起點(diǎn)搜索。如果在“GC roots”和一個(gè)對象之間沒有可達(dá)路徑攘烛,則稱該對象是不可達(dá)的魏滚。要注意的是,不可達(dá)對象不等價(jià)于可回收對象坟漱,不可達(dá)對象變?yōu)榭苫厥諏ο笾辽僖?jīng)過兩次標(biāo)記過程鼠次。兩次標(biāo)記后仍然是可回收對象,則將面臨回收芋齿。

標(biāo)記清除算法(Mark-Sweep)

最基礎(chǔ)的垃圾回收算法腥寇,分為兩個(gè)階段,標(biāo)注和清除沟突。標(biāo)記階段標(biāo)記出所有需要回收的對象花颗,清除階段回收被標(biāo)記的對象所占用的空間。從圖中我們就可以發(fā)現(xiàn)惠拭,該算法最大的問題是內(nèi)存碎片化嚴(yán)重扩劝,后續(xù)可能發(fā)生大對象不能找到可利用空間的問題庸论。 如圖

在這里插入圖片描述

復(fù)制算法(copying)

為了解決Mark-Sweep算法內(nèi)存碎片化的缺陷而被提出的算法。按內(nèi)存容量將內(nèi)存劃分為等大小的兩塊棒呛。每次只使用其中一塊聂示,當(dāng)這一塊內(nèi)存滿后將尚存活的對象復(fù)制到另一塊上去,把已使用的內(nèi)存清掉簇秒,這種算法雖然實(shí)現(xiàn)單鱼喉,內(nèi)存效率高,不易產(chǎn)生碎片趋观,但是最大的問題是可用內(nèi)存被壓縮到了原本的一半扛禽。且存活對象增多的話,Copying算法的效率會大大降低皱坛。 如圖:

在這里插入圖片描述

標(biāo)記整理算法(Mark-Compact)

結(jié)合了以上兩個(gè)算法编曼,為了避免缺陷而提出。標(biāo)記階段和Mark-Sweep算法相同剩辟,標(biāo)記后不是清理對象掐场,而是將存活對象移向內(nèi)存的一端。然后清除端邊界外的對象贩猎。如圖:

在這里插入圖片描述

分代收集算法

分代收集法是目前大部分JVM所采用的方法熊户,其核心思想是根據(jù)對象存活的不同生命周期將內(nèi)存劃分為不同的域,一般情況下將GC堆劃分為老生代(Tenured/Old Generation)和新生代(Young Generation)吭服。老生代的特點(diǎn)是每次垃圾回收時(shí)只有少量對象需要被回收嚷堡,新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量垃圾需要被回收,因此可以根據(jù)不同區(qū)域選擇不同的算法噪馏。

新生代與復(fù)制算法

目前大部分JVM的GC對于新生代都采取Copying算法麦到,因?yàn)樾律忻看卫厥斩家厥沾蟛糠謱ο舐潭匆獜?fù)制的操作比較少欠肾,但通常并不是按照1:1來劃分新生代。一般將新生代劃分為一塊較大的Eden空間和兩個(gè)較小的Survivor空間(From Space, To Space)拟赊,每次使用Eden空間和其中的一塊Survivor空間刺桃,當(dāng)進(jìn)行回收時(shí),將該兩塊空間中還存活的對象復(fù)制到另一塊Survivor空間中吸祟。

[圖片上傳失敗...(image-988327-1600049102658)]

老年代與標(biāo)記復(fù)制算法

而老年代因?yàn)槊看沃换厥丈倭繉ο笊龋蚨捎肕ark-Compact算法。

  1. JAVA虛擬機(jī)提到過的處于方法區(qū)的永生代(Permanet Generation)屋匕,它用來存儲class類葛碧,常量,方法描述等过吻。對永生代的回收主要包括廢棄常量和無用的類进泼。
  2. 對象的內(nèi)存分配主要在新生代的Eden Space和Survivor Space的From Space(Survivor目前存放對象的那一塊)蔗衡,少數(shù)情況會直接分配到老生代。
  3. 當(dāng)新生代的Eden Space和From Space空間不足時(shí)就會發(fā)生一次GC乳绕,進(jìn)行GC后绞惦,Eden Space和From Space區(qū)的存活對象會被挪到To Space,然后將Eden Space和From Space進(jìn)行清理洋措。
  4. 如果To Space無法足夠存儲某個(gè)對象济蝉,則將這個(gè)對象存儲到老生代。
  5. 在進(jìn)行GC后菠发,使用的便是Eden Space和To Space了王滤,如此反復(fù)循環(huán)。
  6. 當(dāng)對象在Survivor區(qū)躲過一次GC后滓鸠,其年齡就會+1淑仆。默認(rèn)情況下年齡到達(dá)15的對象會被移到老生代中。

JAVA 四中引用類型

  • 強(qiáng)引用 :在Java中最常見的就是強(qiáng)引用哥力,把一個(gè)對象賦給一個(gè)引用變量蔗怠,這個(gè)引用變量就是一個(gè)引用。當(dāng)一個(gè)對象被強(qiáng)引用變量引用時(shí)吩跋,它處于可達(dá)狀態(tài)寞射,它是不可能被垃圾回收機(jī)制的,即使該對象以后永遠(yuǎn)都不會被用到JVM也不會回收锌钮。因此強(qiáng)引用是造成Java內(nèi)存泄漏的主要原因之一桥温。
  • 軟引用 :軟引用需要用SoftReference類來實(shí)現(xiàn),對于只有軟引用的對象來說梁丘,當(dāng)系統(tǒng)內(nèi)存足夠它不會被回收侵浸,當(dāng)系統(tǒng)內(nèi)存空間不足時(shí)它會被回收。軟引用通常用在對內(nèi)存敏感的程序中氛谜。
  • 弱引用 :弱引用需要用WeakReference類來實(shí)現(xiàn)掏觉,它比軟引用的生存期更短,對于只有弱引用對象來說值漫,只要垃圾回收機(jī)制一運(yùn)行澳腹,不管 JVM 的內(nèi)存空間是否足夠,總會回收該對象占用的內(nèi)存杨何。
  • 虛引用 :虛引用需要PhantomReference類來實(shí)現(xiàn)酱塔,它不能單獨(dú)使用,必須和引用隊(duì)列聯(lián)合使用危虱。虛引用的主要作用是跟蹤對象被垃圾回收的狀態(tài)羊娃。

GC分代收集算法 VS 分區(qū)收集算法

分代收集算法

當(dāng)前主流VM垃圾收集都采用”分代收集”(Generational Collection)算法, 這種算法會根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊, 如JVM中的 新生代、老年代埃跷、永久代蕊玷,這樣就可以根據(jù)各年代特點(diǎn)分別采用最適當(dāng)?shù)腉C算法

  • 新生代-復(fù)制算法:每次垃圾收集都能發(fā)現(xiàn)大批對象已死, 只有少量存活. 因此選用復(fù)制算法, 只需要付出少量存活對象的復(fù)制成本就可以完成收集.

  • 老年代-標(biāo)記整理算法:因?yàn)閷ο?strong>存活率高芦瘾、沒有額外空間對它進(jìn)行分配擔(dān)保, 就必須采用“標(biāo)記—清理”或“標(biāo)記—整理”算法來進(jìn)行回收, 不必進(jìn)行內(nèi)存復(fù)制, 且直接騰出空閑內(nèi)存.

  • 分區(qū)收集算法 分區(qū)算法:則將整個(gè)堆空間劃分為連續(xù)的不同小區(qū)間, 每個(gè)小區(qū)間獨(dú)立使用, 獨(dú)立回收. 這樣做的好處是可以控制一次回收多少個(gè)小區(qū)間 , 根據(jù)目標(biāo)停頓時(shí)間, 每次合理地回收若干個(gè)小區(qū)間(而不是整個(gè)堆), 從而減少一次GC所產(chǎn)生的停頓

GC垃圾收集器

Java堆內(nèi)存被劃分為新生代和年老代兩部分集畅,新生代主要使用復(fù)制和標(biāo)記-清除垃圾回收算法近弟;老年代主要使用標(biāo)記-整理垃圾回收算法,因此java虛擬中針對新生代和年老代分別提供了多種不同的垃圾收集器挺智,JDK1.6中Sun HotSpot虛擬機(jī)的垃圾收集器如下:


在這里插入圖片描述

Serial垃圾收集器(單線程祷愉、復(fù)制算法)

Serial(英文連續(xù))是最基本垃圾收集器,使用復(fù)制算法赦颇,曾經(jīng)是JDK1.3.1之前新生代唯一的垃圾收集器二鳄。Serial是一個(gè)單線程的收集器,它不但只會使用一個(gè)CPU或一條線程去完成垃圾收集工作媒怯,并且在進(jìn)行垃圾收集的同時(shí)订讼,必須暫停其他所有的工作線程,直到垃圾收集結(jié)束扇苞。

Serial垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作線程欺殿,但是它簡單高效,對于限定單個(gè)CPU環(huán)境來說鳖敷,沒有線程交互的開銷脖苏,可以獲得最高的單線程垃圾收集效率,因此Serial垃圾收集器依然是java虛擬機(jī)運(yùn)行在Client模式下默認(rèn)的新生代垃圾收集器定踱。

ParNew垃圾收集器(Serial+多線程)

ParNew垃圾收集器其實(shí)是Serial收集器的多線程版本棍潘,也使用復(fù)制算法,除了使用多線程進(jìn)行垃圾收集之外崖媚,其余的行為和Serial收集器完全一樣亦歉,ParNew垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程。

ParNew收集器默認(rèn)開啟和CPU數(shù)目相同的線程數(shù)畅哑,可以通過-XX:ParallelGCThreads參數(shù)來限制垃圾收集器的線程數(shù)肴楷。【Parallel:平行的】

ParNew雖然是除了多線程外和Serial收集器幾乎完全一樣敢课,但是ParNew垃圾收集器是很多java虛擬機(jī)運(yùn)行在Server模式下新生代的默認(rèn)垃圾收集器阶祭。

Parallel Scavenge收集器(多線程復(fù)制算法、高效)

Parallel Scavenge收集器也是一個(gè)新生代垃圾收集器直秆,同樣使用復(fù)制算法,也是一個(gè)多線程的垃圾收集器鞭盟,它重點(diǎn)關(guān)注的是程序達(dá)到一個(gè)可控制的吞吐量(Thoughput圾结,CPU用于運(yùn)行用戶代碼的時(shí)間/CPU總消耗時(shí)間,即吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間))齿诉,高吞吐量可以最高效率地利用CPU時(shí)間筝野,盡快地完成程序的運(yùn)算任務(wù)晌姚,主要適用于在后臺運(yùn)算而不需要太多交互的任務(wù)。自適應(yīng)調(diào)節(jié)策略也是ParallelScavenge收集器與ParNew收集器的一個(gè)重要區(qū)別歇竟。

Serial Old收集器(單線程標(biāo)記整理算法 )

Serial Old是Serial垃圾收集器年老代版本挥唠,它同樣是個(gè)單線程的收集器,使用標(biāo)記-整理算法焕议,這個(gè)收集器也主要是運(yùn)行在Client默認(rèn)的java虛擬機(jī)默認(rèn)的年老代垃圾收集器宝磨。

在Server模式下,主要有兩個(gè)用途:

  1. 在JDK1.5之前版本中與新生代的Parallel Scavenge收集器搭配使用盅安。
  2. 作為年老代中使用CMS收集器的后備垃圾收集方案唤锉。

新生代Serial與年老代Serial Old搭配垃圾收集過程圖:

image-20200907145210429.png

新生代Parallel Scavenge收集器與ParNew收集器工作原理類似,都是多線程的收集器别瞭,都使用的是復(fù)制算法窿祥,在垃圾收集過程中都需要暫停所有的工作線程。新生代Parallel Scavenge/ParNew與年老代Serial Old搭配垃圾收集過程圖:

在這里插入圖片描述

Parallel Old收集器(多線程標(biāo)記整理算法)

Parallel Old收集器是Parallel Scavenge的年老代版本蝙寨,使用多線程的標(biāo)記-整理算法晒衩,在JDK1.6才開始提供。

在JDK1.6之前墙歪,新生代使用ParallelScavenge收集器只能搭配年老代的Serial Old收集器浸遗,只能保證新生代的吞吐量優(yōu)先,無法保證整體的吞吐量箱亿,Parallel Old正是為了在年老代同樣提供吞吐量優(yōu)先的垃圾收集器跛锌,如果系統(tǒng)對吞吐量要求比較高,可以優(yōu)先考慮新生代Parallel Scavenge和年老代Parallel Old收集器的搭配策略届惋。

新生代Parallel Scavenge和年老代Parallel Old收集器搭配運(yùn)行過程圖:

在這里插入圖片描述

CMS收集器(多線程標(biāo)記清除算法)

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器髓帽,其最主要目標(biāo)是獲取最短垃圾回收停頓時(shí)間,和其他年老代使用標(biāo)記-整理算法不同脑豹,它使用多線程的標(biāo)記-清除算法郑藏。 最短的垃圾收集停頓時(shí)間可以為交互比較高的程序提高用戶體驗(yàn)。

CMS工作機(jī)制相比其他的垃圾收集器來說更復(fù)雜瘩欺,整個(gè)過程分為以下4個(gè)階段:

  • 初始標(biāo)記 :只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)的對象必盖,速度很快,仍然需要暫停所有的工作線程俱饿。

  • 并發(fā)標(biāo)記 :進(jìn)行GC Roots跟蹤的過程歌粥,和用戶線程一起工作,不需要暫停工作線程拍埠。

  • 重新標(biāo)記 :為了修正在并發(fā)標(biāo)記期間失驶,因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對象的標(biāo)記
    記錄,仍然需要暫停所有的工作線程枣购。

  • 并發(fā)清除 :清除GC Roots不可達(dá)對象嬉探,和用戶線程一起工作擦耀,不需要暫停工作線程。由于耗時(shí)最長的并
    發(fā)標(biāo)記和并發(fā)清除過程中涩堤,垃圾收集線程可以和用戶現(xiàn)在一起并發(fā)工作眷蜓,所以總體上來看CMS收集器的內(nèi)存回收和用戶線程是一起并發(fā)地執(zhí)行。

CMS收集器工作過程:

image-20200907150334084.png

G1收集器

Garbage first垃圾收集器是目前垃圾收集器理論發(fā)展的最前沿成果胎围,相比與CMS收集器吁系,G1收
集器兩個(gè)最突出的改進(jìn)是:

  1. 基于標(biāo)記-整理算法,不產(chǎn)生內(nèi)存碎片痊远。
  2. 可以非常精確控制停頓時(shí)間垮抗,在不犧牲吞吐量前提下,實(shí)現(xiàn)低停頓垃圾回收碧聪。

G1收集器避免全區(qū)域垃圾收集冒版,它把堆內(nèi)存劃分為大小固定的幾個(gè)獨(dú)立區(qū)域,并且跟蹤這些區(qū)域的垃圾收集進(jìn)度逞姿,同時(shí)在后臺維護(hù)一個(gè)優(yōu)先級列表辞嗡,每次根據(jù)所允許的收集時(shí)間,優(yōu)先回收垃圾****最多的區(qū)域滞造。區(qū)域劃分和優(yōu)先級區(qū)域回收機(jī)制续室,確保G1收集器可以在有限時(shí)間獲得最高的垃圾收集效率。

JAVA IO/NIO

阻塞IO模型

最傳統(tǒng)的一種IO模型谒养,即在讀寫數(shù)據(jù)過程中會發(fā)生阻塞現(xiàn)象挺狰。當(dāng)用戶線程發(fā)出IO請求之后,內(nèi)核會去查看數(shù)據(jù)是否就緒买窟,如果沒有就緒就會等待數(shù)據(jù)就緒丰泊,而用戶線程就會處于阻塞狀態(tài),用戶線程交出CPU始绍。當(dāng)數(shù)據(jù)就緒之后瞳购,內(nèi)核會將數(shù)據(jù)拷貝到用戶線程,并返回結(jié)果給用戶線程亏推,用戶線程才解除block狀態(tài)学赛。典型的阻塞IO模型的例子為:data = socket.read();如果數(shù)據(jù)沒有就緒,就會一直阻塞在read方法吞杭。

非阻塞IO模型

當(dāng)用戶線程發(fā)起一個(gè)read操作后盏浇,并不需要等待,而是馬上就得到了一個(gè)結(jié)果篇亭。如果結(jié)果是一個(gè)error時(shí)缠捌,它就知道數(shù)據(jù)還沒有準(zhǔn)備好,于是它可以再次發(fā)送read操作译蒂。一旦內(nèi)核中的數(shù)據(jù)準(zhǔn)備好了曼月,并且又再次收到了用戶線程的請求,那么它馬上就將數(shù)據(jù)拷貝到了用戶線程柔昼,然后返回哑芹。所以事實(shí)上,在非阻塞IO模型中捕透,用戶線程需要不斷地詢問內(nèi)核數(shù)據(jù)是否就緒聪姿,也就說非阻塞IO不會交出CPU,而會一直占用CPU乙嘀。典型的非阻塞IO模型一般如下:

while(true){ 
    data = socket.read(); 
    if(data!= error){ 
        //處理數(shù)據(jù) 
        break; 
    } 
} 

但是對于非阻塞IO就有一個(gè)非常嚴(yán)重的問題末购,在while循環(huán)中需要不斷地去詢問內(nèi)核數(shù)據(jù)是否就緒,這樣會導(dǎo)致CPU占用率非常高虎谢,因此一般情況下很少使用while循環(huán)這種方式來讀取數(shù)據(jù)盟榴。

多路復(fù)用IO模型

多路復(fù)用IO模型是目前使用得比較多的模型。Java NIO實(shí)際上就是多路復(fù)用IO婴噩。在多路復(fù)用IO模型中擎场,會有一個(gè)線程不斷去輪詢多個(gè)socket的狀態(tài),只有當(dāng)socket真正有讀寫事件時(shí)几莽,才真正調(diào)用實(shí)際的IO讀寫操作迅办。因?yàn)樵诙嗦窂?fù)用IO模型中,只需要使用一個(gè)線程就可以管理多個(gè)socket章蚣,系統(tǒng)不需要建立新的進(jìn)程或者線程站欺,也不必維護(hù)這些線程和進(jìn)程,并且只有在真正有socket讀寫事件進(jìn)行時(shí)纤垂,才會使用IO資源矾策,所以它大大減少了資源占用。在Java NIO中洒忧,是通過selector.select()去查詢每個(gè)通道是否有到達(dá)事件蝴韭,如果沒有事件,則一直阻塞在那里熙侍,因此這種方式會導(dǎo)致用戶線程的阻塞榄鉴。多路復(fù)用IO模式,通過一個(gè)線程就可以管理多個(gè)socket蛉抓,只有當(dāng)socket真正有讀寫事件發(fā)生才會占用資源來進(jìn)行實(shí)際的讀寫操作庆尘。因此,多路復(fù)用IO比較適合連接數(shù)比較多的情況巷送。

另外多路復(fù)用IO為何比非阻塞IO模型的效率高是因?yàn)樵诜亲枞鸌O中驶忌,不斷地詢問socket狀態(tài)時(shí)通過用戶線程去進(jìn)行的,而在多路復(fù)用IO中,輪詢每個(gè)socket狀態(tài)是內(nèi)核在進(jìn)行的付魔,這個(gè)效率要比用戶線程要高的多聊品。

不過要注意的是,多路復(fù)用IO模型是通過輪詢的方式來檢測是否有事件到達(dá)几苍,并且對到達(dá)的事件逐一進(jìn)行響應(yīng)翻屈。因此對于多路復(fù)用IO模型來說,一旦事件響應(yīng)體很大妻坝,那么就會導(dǎo)致后續(xù)的事件遲遲得不到處理伸眶,并且會影響新的事件輪詢。

信號驅(qū)動(dòng)IO模型

在信號驅(qū)動(dòng)IO模型中刽宪,當(dāng)用戶線程發(fā)起一個(gè)IO請求操作厘贼,會給對應(yīng)的socket注冊一個(gè)信號函
數(shù),然后用戶線程會繼續(xù)執(zhí)行圣拄,當(dāng)內(nèi)核數(shù)據(jù)就緒時(shí)會發(fā)送一個(gè)信號給用戶線程嘴秸,用戶線程接收到
信號之后,便在信號函數(shù)中調(diào)用IO讀寫操作來進(jìn)行實(shí)際的IO請求操作售担。

異步IO模型

異步IO模型才是最理想的IO模型赁遗,在異步IO模型中,當(dāng)用戶線程發(fā)起read操作之后族铆,立刻就可以開始去做其它的事岩四。而另一方面,從內(nèi)核的角度哥攘,當(dāng)它受到一個(gè)asynchronous read之后剖煌,它會立刻返回,說明read請求已經(jīng)成功發(fā)起了逝淹,因此不會對用戶線程產(chǎn)生任何block耕姊。然后,內(nèi)核會等待數(shù)據(jù)準(zhǔn)備完成栅葡,然后將數(shù)據(jù)拷貝到用戶線程茉兰,當(dāng)這一切都完成之后,內(nèi)核會給用戶線程發(fā)送一個(gè)信號欣簇,告訴它read操作完成了规脸。也就說用戶線程完全不需要實(shí)際的整個(gè)IO操作是如何進(jìn)行的,只需要先發(fā)起一個(gè)請求熊咽,當(dāng)接收內(nèi)核返回的成功信號時(shí)表示IO操作已經(jīng)完成莫鸭,可以直接去使用數(shù)據(jù)了。

也就說在異步IO模型中横殴,IO操作的兩個(gè)階段都不會阻塞用戶線程,這兩個(gè)階段都是由內(nèi)核自動(dòng)完成,然后發(fā)送一個(gè)信號告知用戶線程操作已完成梨与。用戶線程中不需要再次調(diào)用IO函數(shù)進(jìn)行具體的讀寫堕花。這點(diǎn)是和信號驅(qū)動(dòng)模型有所不同的,在信號驅(qū)動(dòng)模型中蛋欣,當(dāng)用戶線程接收到信號表示數(shù)據(jù)已經(jīng)就緒航徙,然后需要用戶線程調(diào)用IO函數(shù)進(jìn)行實(shí)際的讀寫操作如贷;而在異步IO模型中陷虎,收到信號表示IO操作已經(jīng)完成,不需要再在用戶線程中調(diào)用IO函數(shù)進(jìn)行實(shí)際的讀寫操作杠袱。

注意尚猿,異步IO是需要操作系統(tǒng)的底層支持,在Java 7中楣富,提供了Asynchronous IO凿掂。

JAVA IO包

在這里插入圖片描述

JAVA NIO

NIO主要有三大核心部分:Channel(通道),Buffer(緩沖區(qū)), Selector纹蝴。傳統(tǒng)IO基于字節(jié)流和字符流進(jìn)行操作庄萎,而NIO基于Channel和Buffer(緩沖區(qū))進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中塘安,或者從緩沖區(qū)寫入到通道中糠涛。Selector(選擇區(qū))用于監(jiān)聽多個(gè)通道的事件(比如:連接打開,數(shù)據(jù)到達(dá))兼犯。因此忍捡,單個(gè)線程可以監(jiān)聽多個(gè)數(shù)據(jù)通道。

在這里插入圖片描述

NIO和傳統(tǒng)IO之間第一個(gè)最大的區(qū)別是切黔,IO是面向流的砸脊,NIO是面向緩沖區(qū)的

NIO的緩沖區(qū)

Java IO面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié)纬霞,直至讀取所有字節(jié)凌埂,它們沒有被緩存在任何地方。此外诗芜,它不能前后移動(dòng)流中的數(shù)據(jù)瞳抓。如果需要前后移動(dòng)從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)绢陌。

NIO的緩沖導(dǎo)向方法不同挨下。數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動(dòng)脐湾。這就增加了處理過程中的靈活性臭笆。但是,還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數(shù)據(jù)。而且愁铺,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時(shí)鹰霍,不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

NIO的非阻塞

IO的各種流是阻塞的茵乱。這意味著茂洒,當(dāng)一個(gè)線程調(diào)用read() 或 write()時(shí),該線程被阻塞瓶竭,直到有一些數(shù)據(jù)被讀取督勺,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了斤贰。

NIO的非阻塞模式智哀,使一個(gè)線程從某通道發(fā)送請求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù)荧恍,如果目前沒有數(shù)據(jù)可用時(shí)瓷叫,就什么都不會獲取。而不是保持線程阻塞送巡,所以直至數(shù)據(jù)變的可以讀取之前摹菠,該線程可以繼續(xù)做其他的事情。

非阻塞寫也是如此骗爆。一個(gè)線程請求寫入一些數(shù)據(jù)到某通道次氨,但不需要等待它完全寫入,這個(gè)線程同時(shí)可以去做別的事情淮腾。 線程通常將非阻塞IO的空閑時(shí)間用于在其它通道上執(zhí)行IO操作糟需,所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道(channel)。

在這里插入圖片描述

Channel

首先說一下Channel谷朝,國內(nèi)大多翻譯成“通道”洲押。Channel和IO中的Stream(流)是差不多一個(gè)等級的。只不過Stream是單向的圆凰,譬如:InputStream, OutputStream杈帐,而Channel是雙向的,既可以用來進(jìn)行讀操作,又可以用來進(jìn)行寫操作。 NIO中的Channel的主要實(shí)現(xiàn)有:

  1. FileChannel
  2. DatagramChannel
  3. SocketChannel
  4. ServerSocketChannel

這里看名字就可以猜出個(gè)所以然來:分別可以對應(yīng)文件IO抑淫、UDP和TCP(Server和Client)段化。

Buffer

Buffer搁吓,故名思意,緩沖區(qū),實(shí)際上是一個(gè)容器,是一個(gè)連續(xù)數(shù)組尽楔。Channel提供從文件投储、網(wǎng)絡(luò)讀取數(shù)據(jù)的渠道,但是讀取或?qū)懭氲臄?shù)據(jù)都必須經(jīng)由Buffer阔馋。

[圖片上傳失敗...(image-478aff-1600049102658)]

上面的圖描述了從一個(gè)客戶端向服務(wù)端發(fā)送數(shù)據(jù)玛荞,然后服務(wù)端接收數(shù)據(jù)的過程∨磺蓿客戶端發(fā)送數(shù)據(jù)時(shí)勋眯,必須先將數(shù)據(jù)存入Buffer中,然后將Buffer中的內(nèi)容寫入通道下梢。服務(wù)端這邊接收數(shù)據(jù)必須通過Channel將數(shù)據(jù)讀入到Buffer中客蹋,然后再從Buffer中取出數(shù)據(jù)來處理。

在NIO中怔球,Buffer是一個(gè)頂層父類嚼酝,它是一個(gè)抽象類,常用的Buffer的子類有:ByteBuffer竟坛、IntBuffer、 CharBuffer钧舌、 LongBuffer担汤、 DoubleBuffer、FloatBuffer洼冻、ShortBuffer

Selector

Selector類是NIO的核心類崭歧,Selector能夠檢測多個(gè)注冊的通道上是否有事件發(fā)生,如果有事件發(fā)生撞牢,便獲取事件然后針對每個(gè)事件進(jìn)行相應(yīng)的響應(yīng)處理率碾。這樣一來,只是用一個(gè)單線程就可以管理多個(gè)通道屋彪,也就是管理多個(gè)連接所宰。這樣使得只有在連接真正有讀寫事件發(fā)生時(shí),才會調(diào)用函數(shù)來進(jìn)行讀寫畜挥,就大大地減少了系統(tǒng)開銷仔粥,并且不必為每個(gè)連接都創(chuàng)建一個(gè)線程,不用去維護(hù)多個(gè)線程蟹但,并且避免了多線程之間的上下文切換導(dǎo)致的開銷躯泰。

JVM 類加載機(jī)制

JVM類加載機(jī)制分為五個(gè)部分:加載,驗(yàn)證华糖,準(zhǔn)備麦向,解析,初始化客叉,下面我們就分別來看一下這五個(gè)過程诵竭。

在這里插入圖片描述
  1. 加載:加載是類加載過程中的一個(gè)階段景描,這個(gè)階段會在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對
    象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的入口秀撇。
    • 注意這里不一定非得要從一個(gè)Class文件獲取超棺,這里既可以從ZIP包中讀取(比如從jar包和war包中讀群茄唷)棠绘,也可以在運(yùn)行時(shí)計(jì)算生成(動(dòng)態(tài)代理),也可以由其它文件生成(比如將JSP文件轉(zhuǎn)換成對應(yīng)的Class類)再扭。
  2. 驗(yàn)證 :這一階段的主要目的是為了確保Class文件的字節(jié)流中包含的信息是否符合當(dāng)前虛擬機(jī)的要求氧苍,并
    且不會危害虛擬機(jī)自身的安全。
  3. 準(zhǔn)備 :準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量的初始值階段泛范,即在方法區(qū)中分配這些變量所使
    用的內(nèi)存空間让虐。注意這里所說的初始值概念,比如一個(gè)類變量定義為: public static int v = 8080; 實(shí)際上變量v在準(zhǔn)備階段過后的初始值為0而不是8080罢荡,將v賦值為8080的put static指令是程序被編譯后赡突,存放于類構(gòu)造器<client>方法之中。 但是注意如果聲明為: public static final int v = 8080; 在編譯階段會為v生成ConstantValue屬性区赵,在準(zhǔn)備階段虛擬機(jī)會根據(jù)ConstantValue屬性將v賦值為8080惭缰。
  4. 解析 :解析階段是指虛擬機(jī)將常量池中的符號引用替換為直接引用的過程。符號引用就是class文件中的: CONSTANT_Class_info 笼才、CONSTANT_Field_info 漱受、CONSTANT_Method_info 等類型的常量。
  5. 符號引用 :符號引用與虛擬機(jī)實(shí)現(xiàn)的布局無關(guān)骡送,引用的目標(biāo)并不一定要已經(jīng)加載到內(nèi)存中昂羡。各種虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局可以各不相同,但是它們能接受的符號引用必須是一致的摔踱,因?yàn)榉栆玫淖置媪啃问矫鞔_定義在Java虛擬機(jī)規(guī)范的Class文件格式中虐先。
  6. 直接引用 :直接引用可以是指向目標(biāo)的指針,相對偏移量或是一個(gè)能間接定位到目標(biāo)的句柄昌渤。如果有了直接引用赴穗,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。
  7. 初始化:初始化階段是類加載最后一個(gè)階段膀息,前面的類加載階段之后般眉,除了在加載階段可以自定義類加載器以外,其它操作都由JVM主導(dǎo)潜支。到了初始階段甸赃,才開始真正執(zhí)行類中定義的Java程序代碼。
  8. 類構(gòu)造器<client> :初始化階段是執(zhí)行類構(gòu)造器<client>方法的過程冗酿。<client>方法是由編譯器自動(dòng)收集類中的類變量的賦值操作和靜態(tài)語句塊中的語句合并而成的埠对。虛擬機(jī)會保證子<client>方法執(zhí)行之前络断,父類的<client>方法已經(jīng)執(zhí)行完畢,如果一個(gè)類中沒有對靜態(tài)變量賦值也沒有靜態(tài)語句塊项玛,那么編譯器可以不為這個(gè)類生成<client>()方法貌笨。 注意以下幾種情況不會執(zhí)行類初始化:
    1. 通過子類引用父類的靜態(tài)字段,只會觸發(fā)父類的初始化襟沮,而不會觸發(fā)子類的初始化锥惋。
    2. 定義對象數(shù)組,不會觸發(fā)該類的初始化开伏。
    3. 常量在編譯期間會存入調(diào)用類的常量池中膀跌,本質(zhì)上并沒有直接引用定義常量的類,不會觸
      發(fā)定義常量所在的類固灵。
    4. 通過類名獲取Class對象捅伤,不會觸發(fā)類的初始化。
    5. 通過Class.forName加載指定類時(shí)巫玻,如果指定參數(shù)initialize為false時(shí)丛忆,也不會觸發(fā)類初
      始化,其實(shí)這個(gè)參數(shù)是告訴虛擬機(jī)大审,是否要對類進(jìn)行初始化蘸际。
    6. 通過ClassLoader默認(rèn)的loadClass方法,也不會觸發(fā)初始化動(dòng)作徒扶。

類加載器

虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把加載動(dòng)作放到JVM外部實(shí)現(xiàn),以便讓應(yīng)用程序決定如何獲取所需的類根穷,JVM提供了3種類加載器:

啟動(dòng)類加載器(Bootstrap ClassLoader) :負(fù)責(zé)加載 JAVA_HOME\lib 目錄中的姜骡,或通過-Xbootclasspath參數(shù)指定路徑中的,且被虛擬機(jī)認(rèn)可(按文件名識別屿良,如rt.jar)的類圈澈。

擴(kuò)展類加載器(Extension ClassLoader) :負(fù)責(zé)加載 JAVA_HOME\lib\ext 目錄中的,或通過java.ext.dirs系統(tǒng)變量指定路徑中的類庫尘惧。

應(yīng)用程序類加載器(Application ClassLoader): 負(fù)責(zé)加載用戶路徑(classpath)上的類庫康栈。 JVM通過雙親委派模型進(jìn)行類的加載,當(dāng)然我們也可以通過繼承java.lang.ClassLoader實(shí)現(xiàn)自定義的類加載器喷橙。

在這里插入圖片描述

雙親委派

當(dāng)一個(gè)類收到了類加載請求啥么,他首先不會嘗試自己去加載這個(gè)類,而是把這個(gè)請求委派給父類去完成贰逾,每一個(gè)層次類加載器都是如此悬荣,因此所有的加載請求都應(yīng)該傳送到啟動(dòng)類加載其中,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個(gè)請求的時(shí)候(在它的加載路徑下沒有找到所需加載的Class)疙剑,子類加載器才會嘗試自己去加載氯迂。

采用雙親委派的一個(gè)好處是比如加載位于rt.jar包中的類java.lang.Object践叠,不管是哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的啟動(dòng)類加載器進(jìn)行加載嚼蚀,這樣就保證了使用不同的類加載器最終得到的都是同樣一個(gè)Object對象禁灼。

在這里插入圖片描述

OSGI(動(dòng)態(tài)模型系統(tǒng))

OSGi(Open Service Gateway Initiative),是面向Java的動(dòng)態(tài)模型系統(tǒng)轿曙,是Java動(dòng)態(tài)化模塊化系統(tǒng)的一系列規(guī)范弄捕。

動(dòng)態(tài)改變構(gòu)造

OSGi服務(wù)平臺提供在多種網(wǎng)絡(luò)設(shè)備上無需重啟的動(dòng)態(tài)改變構(gòu)造的功能。為了最小化耦合度和促使這些耦合度可管理拳芙,OSGi技術(shù)提供一種面向服務(wù)的架構(gòu)察藐,它能使這些組件動(dòng)態(tài)地發(fā)現(xiàn)對方。

模塊化編程與熱插拔

OSGi旨在為實(shí)現(xiàn)Java程序的模塊化編程提供基礎(chǔ)條件舟扎,基于OSGi的程序很可能可以實(shí)現(xiàn)模塊級的熱插拔功能分飞,當(dāng)程序升級更新時(shí),可以只停用睹限、重新安裝然后啟動(dòng)程序的其中一部分譬猫,這對企業(yè)級程序開發(fā)來說是非常具有誘惑力的特性。

OSGi描繪了一個(gè)很美好的模塊化開發(fā)目標(biāo)羡疗,而且定義了實(shí)現(xiàn)這個(gè)目標(biāo)的所需要服務(wù)與架構(gòu)染服,同時(shí)也有成熟的框架進(jìn)行實(shí)現(xiàn)支持。但并非所有的應(yīng)用都適合采用OSGi作為基礎(chǔ)架構(gòu)叨恨,它在提供強(qiáng)大功能同時(shí)柳刮,也引入了額外的復(fù)雜度,因?yàn)樗蛔袷亓祟惣虞d的雙親委托模型痒钝。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秉颗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子送矩,更是在濱河造成了極大的恐慌蚕甥,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栋荸,死亡現(xiàn)場離奇詭異菇怀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晌块,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門爱沟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人摸袁,你說我怎么就攤上這事钥顽。” “怎么了靠汁?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵蜂大,是天一觀的道長闽铐。 經(jīng)常有香客問我,道長奶浦,這世上最難降的妖魔是什么兄墅? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮澳叉,結(jié)果婚禮上隙咸,老公的妹妹穿的比我還像新娘。我一直安慰自己成洗,他們只是感情好五督,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓶殃,像睡著了一般充包。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遥椿,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天基矮,我揣著相機(jī)與錄音,去河邊找鬼冠场。 笑死家浇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碴裙。 我是一名探鬼主播钢悲,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舔株!你這毒婦竟也來了譬巫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤督笆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后诱贿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娃肿,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年珠十,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了料扰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焙蹭,死狀恐怖晒杈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孔厉,我是刑警寧澤拯钻,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布帖努,位于F島的核電站,受9級特大地震影響粪般,放射性物質(zhì)發(fā)生泄漏拼余。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一亩歹、第九天 我趴在偏房一處隱蔽的房頂上張望匙监。 院中可真熱鬧,春花似錦小作、人聲如沸亭姥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽达罗。三九已至,卻和暖如春础拨,著一層夾襖步出監(jiān)牢的瞬間氮块,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工诡宗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留滔蝉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓塔沃,卻偏偏與公主長得像蝠引,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蛀柴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容