摘要:的字節(jié)碼解釋器和編譯器使用寫屏障維護(hù)卡表。解釋器每次執(zhí)行更新引用的字節(jié)碼時(shí),都會(huì)執(zhí)行一段寫屏障,編譯器在生成更新引用的代碼后,也會(huì)生成一段寫屏障。
4. JVM 4.1 GC 1. 垃圾收集
基礎(chǔ) : 可達(dá)性分析算法 GC ROOTS
復(fù)制算法
標(biāo)記清除
標(biāo)記整理
分代收集 -- 1. 新生代 ; 2.3 老年代
注: Oop Map -- 安全點(diǎn) -- 安全區(qū)
以下部分內(nèi)容 來自 這個(gè)博主的文章
標(biāo)記清除法/標(biāo)記壓縮法、復(fù)制收集算法、引用計(jì)數(shù)法
這里的 引用計(jì)數(shù)法 因?yàn)闀兄v解少,所以講一下:
引用計(jì)數(shù)法,它的基本原理是,在每個(gè)對象中保存該對象的引用計(jì)數(shù),當(dāng)引用發(fā)生增減時(shí)對計(jì)數(shù)進(jìn)行更新。引用計(jì)數(shù)的增減,一般發(fā)生在變量賦值、對象內(nèi)容更新、函數(shù)結(jié)束(局部變量不再被引用)等時(shí)間點(diǎn)。當(dāng)一個(gè)對象的引用計(jì)數(shù)變?yōu)?時(shí),則說明它將來不會(huì)再被引用,因此可以釋放相應(yīng)的內(nèi)存空間。
缺點(diǎn):
無法釋放循環(huán)引用的對象。
必須在引用發(fā)生增減時(shí)對引用計(jì)數(shù)做出正確的增減,而如果漏掉了某個(gè)增減的話,就會(huì)引發(fā)很難找到原因的內(nèi)存錯(cuò)誤。引用數(shù)忘了增加的話,會(huì)對不恰當(dāng)?shù)膶ο筮M(jìn)行釋放;而引用數(shù)忘了減少的話,對象會(huì)一直殘留在內(nèi)存中,從而導(dǎo)致內(nèi)存泄漏。
引用計(jì)數(shù)管理并不適合并行處理: 就如同 ConcurrenHashMap源碼分析 中的算法一樣,無法在并行情況下對數(shù)量進(jìn)行準(zhǔn)確的計(jì)算。
分代回收
分代回收的目的,正是為了在程序運(yùn)行期間,將GC所消耗的時(shí)間盡量縮短。
分代回收的基本思路,是利用了一般性程序所具備的性質(zhì),即大部分對象都會(huì)在短時(shí)間內(nèi)成為垃圾,而經(jīng)過一定時(shí)間依然存活的對象往往擁有較長的壽命。
HotSpot 虛擬機(jī)中,在新生代用復(fù)制算法,老年代使用標(biāo)記清除/整理算法。
問題:如果存在老生代對象對新生代對象的引用。如果只掃描新生代區(qū)域的話,那么從老生代對新生代的引用就不會(huì)被檢測到。
這樣一來,如果一個(gè)年輕的對象只有來自老生代對象的引用,就會(huì)被誤認(rèn)為已經(jīng)“死亡”了。
因此,在分代回收中,會(huì)對對象的更新進(jìn)行監(jiān)視,將從老生代對新生代的引用,
記錄在一個(gè)叫做記錄集 Rset(remembered set)的表中。在執(zhí)行小回收(Minor Gc)的過程中,這個(gè)記錄集也作為一個(gè)根來對待。
解決方案:在老生代到新生代的引用產(chǎn)生的瞬間,就必須對該引用進(jìn)行記錄,而負(fù)責(zé)執(zhí)行這個(gè)操作的子程序,需要被嵌入到所有涉及對象更新操作的地方。
這個(gè)負(fù)責(zé)記錄引用的子程序是這樣工作的。設(shè)有兩個(gè)對象:A和B,當(dāng)對A的內(nèi)容進(jìn)行改寫,并加入對B的引用時(shí),
如果①A屬于老生代對象,②B屬于新生代對象,則將該引用添加到記錄集中。
這種檢查程序需要對所有涉及修改對象內(nèi)容的地方進(jìn)行保護(hù),因此被稱為寫屏障(Write barrier)。
增量回收
為了維持程序的實(shí)時(shí)性,不等到GC全部完成,而是將GC操作細(xì)分成多個(gè)部分逐一執(zhí)行。這種方式被稱為增量回收
并行回收
并行回收的基本原理是,是在原有的程序運(yùn)行的同時(shí)進(jìn)行GC操作,這一點(diǎn)和增量回收是相似的。
不過,相對于在一個(gè)CPU上進(jìn)行GC任務(wù)分割的增量回收來說,并行回收可以利用多CPU的性能,盡可能讓這些GC任務(wù)并行(同時(shí))進(jìn)行。
為了支持高頻率的新生代的回收,虛擬機(jī)使用一種叫做卡表(Card Table)的數(shù)據(jù)結(jié)構(gòu).
卡表作為一個(gè)比特位的集合,每一個(gè)比特位可以用來表示年老代的某一區(qū)域中的所有對象是否持有新生代對象的引用。
一、作用
卡表中每一個(gè)位表示年老代4K的空間,
卡表記錄為 0 的年老代區(qū)域沒有任何對象指向新生代,
卡表記錄為 1 的區(qū)域才有對象包含新生代引用,
因此在新生代GC時(shí),只需要掃描卡表位為1所在的年老代空間。使用這種方式,可以大大加快新生代的回收速度。
二、結(jié)構(gòu)
卡表是個(gè)單字節(jié)數(shù)組,每個(gè)數(shù)組元素對應(yīng)堆中的一張卡。
每次年老代對象中某個(gè)引用新生代的字段發(fā)生變化時(shí),Hotspot VM就必須將該卡所對應(yīng)的卡表元素設(shè)置為適當(dāng)?shù)闹?,從而將該引用字段所在的?strong>標(biāo)記為臟。
如下圖:
在Minor GC過程中,垃圾收集器只會(huì)在臟卡中掃描查找年老代-新生代引用。
Hotspot VM的字節(jié)碼解釋器和JIT編譯器使用寫屏障 維護(hù)卡表。
寫屏障 (Write barrier) 是一小段將卡狀態(tài)設(shè)置為臟的代碼。 解釋器每次執(zhí)行更新引用的字節(jié)碼時(shí),都會(huì)執(zhí)行一段寫屏障,JIT編譯器在生成更新引用的代碼后,也會(huì)生成一段寫屏障。
雖然寫屏障使得應(yīng)用線程增加了 -- 性能開銷,但Minor GC變快了許多,整體的垃圾收集效率也提高了許多,通常應(yīng)用的吞吐量也會(huì)有所改善。
1、 吞吐量
應(yīng)用系統(tǒng)的生命周期內(nèi),應(yīng)用程序所花費(fèi)的時(shí)間和系統(tǒng)總運(yùn)行時(shí)間的比值
系統(tǒng)總運(yùn)行時(shí)間=應(yīng)用程序耗時(shí)+GC耗時(shí)
2、 垃圾回收器負(fù)載
垃圾回收器負(fù)載=GC耗時(shí)/系統(tǒng)總運(yùn)行時(shí)間
3、 停頓時(shí)間
垃圾回收器運(yùn)行時(shí),應(yīng)用程序的暫停時(shí)間
4、 垃圾回收頻率
垃圾回收器多長時(shí)間運(yùn)行一次。一般而言,頻率越低越好,通常增大堆空間可以有效降低垃圾回收發(fā)生的頻率,但是會(huì)增加回收時(shí)產(chǎn)生的停頓時(shí)間。
5、 反應(yīng)時(shí)間
當(dāng)一個(gè)對象成為垃圾后,多長時(shí)間內(nèi),它所占用的內(nèi)存空間會(huì)被釋放掉。
-Xms 堆大小
-Xmx 可擴(kuò)展大小
-Xmn 老年代大小
-XX:SurvivorRatio Eden 區(qū)與 Survivor 區(qū)大小比例
注: surivor 區(qū)分為 from 區(qū)與 to 區(qū)
- 在GC開始的時(shí)候,對象只會(huì)存在于Eden區(qū)和名為“From”的Survivor區(qū),Survivor區(qū)“To”是空的。 - 緊接著進(jìn)行GC,Eden區(qū)中所有存活的對象都會(huì)被復(fù)制到“To”,而在“From”區(qū)中,仍存活的對象會(huì)根據(jù)他們的年齡值來決定去向。 - 年齡達(dá)到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設(shè)置)的對象會(huì)被移動(dòng)到年老代中,沒有達(dá)到閾值的對象會(huì)被復(fù)制到“To”區(qū)域 - 經(jīng)過這次GC后,Eden區(qū)和From區(qū)已經(jīng)被清空。這個(gè)時(shí)候,“From”和“To”會(huì)交換他們的角色,也就是新的“To”就是上次GC前的“From” - 新的“From”就是上次GC前的“To”。 - 不管怎樣,都會(huì)保證名為To的Survivor區(qū)域是空的。Minor GC會(huì)一直重復(fù)這樣的過程,直到“To”區(qū)被填滿,“To”區(qū)被填滿之后,會(huì)將所有對象移動(dòng)到年老代中。
大對象直接進(jìn)入老年代 :很長的字符串以及數(shù)組
長期存活的對象進(jìn)入老年代 -XX:MaxTenuringThreshold
動(dòng)態(tài)對象年齡判定 :如果在Survivor 中,相同年齡所有對象的大小總和大于 Survivor 空間的一半, 大于或等于此年齡的對象就可以直接進(jìn)入老年代。
分配擔(dān)保機(jī)制
檢查老年代最大可用連續(xù)空間 與 新生代所有對象的總空間 --> yes --> MinorGc
HandlePromotionFailure 是否允許擔(dān)保失敗 --> yes --> 檢查老年代最大可用連續(xù)空間是否大于歷次晉升到老年代對象的平均大小 --> MinorGC
Minor GC觸發(fā)條件:當(dāng)Eden區(qū)滿時(shí),觸發(fā)Minor GC。
Full GC觸發(fā)條件:
(1)調(diào)用System.gc時(shí),系統(tǒng)建議執(zhí)行Full GC,但是不必然執(zhí)行
(2)老年代空間不足
(3)方法去空間不足
(4)通過Minor GC后進(jìn)入老年代的平均大小大于老年代的可用內(nèi)存
(5)由Eden區(qū)、From Space區(qū)向To Space區(qū)復(fù)制時(shí),對象大小大于To Space可用內(nèi)存,則把該對象轉(zhuǎn)存到老年代,且老年代的可用內(nèi)存小于該對象大小
4個(gè)步驟:
初始標(biāo)記:標(biāo)記 GC ROOTS 可以直接關(guān)聯(lián)的對象
并發(fā)標(biāo)記:GC TRACING
重新標(biāo)記:修正并發(fā)標(biāo)記期間,用戶程序繼續(xù)動(dòng)作而導(dǎo)致的標(biāo)記產(chǎn)生變動(dòng)的那一部分對象的標(biāo)記記錄
并發(fā)清除
3個(gè)缺點(diǎn):
對 CPU 資源非常敏感
無法處理浮動(dòng)垃圾(并發(fā)清理階段,用戶線程仍舊在運(yùn)行,因此一直在產(chǎn)生垃圾,而無法在當(dāng)次收集中處理掉它們)
產(chǎn)生大量的空間碎片
4個(gè)特點(diǎn):
并行與并發(fā): 使用多個(gè) CPU 或 CPU 核心來縮短 Stop-The-World 停頓的時(shí)間
分代收集
空間整合: 基于標(biāo)記-整理算法
可預(yù)測的停頓: 可以建立可預(yù)測的停頓時(shí)間模型,讓使用者明確指定在一個(gè)長度為 M 毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不超過 N 毫秒。
4個(gè)步驟:
初始標(biāo)記
并發(fā)標(biāo)記
最終標(biāo)記
篩選回收: 首先對各個(gè) Region 的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶所期望的 GC 停頓時(shí)間來制定回收計(jì)劃。
G1的GC模式
Young GC:選定所有年輕代里的Region。通過控制年輕代的region個(gè)數(shù),即年輕代內(nèi)存大小,來控制young GC的時(shí)間開銷。
Mixed GC:選定所有年輕代里的Region,外加根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的若干老年代Region。在用戶指定的開銷目標(biāo)范圍內(nèi)盡可能選擇收益高的老年代Region。
注意:Mixed GC不是full GC,它只能回收部分老年代的Region,如果mixed GC實(shí)在無法跟上程序分配內(nèi)存的速度,導(dǎo)致老年代填滿無法繼續(xù)進(jìn)行Mixed GC,就會(huì)使用serial old GC(full GC)來收集整個(gè)GC heap。
global concurrent marking:類似CMS,為Mixed GC提供標(biāo)記服務(wù)。
四個(gè)過程:
初始標(biāo)記(initial mark,STW)。它標(biāo)記了從GC Root開始直接可達(dá)的對象。
并發(fā)標(biāo)記(Concurrent Marking)。這個(gè)階段從GC Root開始對heap中的對象標(biāo)記,標(biāo)記線程與應(yīng)用程序線程并行執(zhí)行,并且收集各個(gè)Region的存活對象信息。
最終標(biāo)記(Remark,STW)。標(biāo)記那些在并發(fā)標(biāo)記階段發(fā)生變化的對象,將被回收。
清除垃圾(Cleanup)。清除空Region(沒有存活對象的),加入到free list。
G1 中的幾個(gè)重要概念 -- 原文鏈接--美團(tuán)點(diǎn)評
一、Region
傳統(tǒng)的GC收集器將連續(xù)的內(nèi)存空間劃分為新生代、老年代和永久代(JDK 8去除了永久代,引入了元空間Metaspace),這種劃分的特點(diǎn)是各代的存儲(chǔ)地址(邏輯地址,下同)是連續(xù)的。
如下圖所示:
而G1的各代存儲(chǔ)地址是不連續(xù)的,每一代都使用了n個(gè)不連續(xù)的大小相同的Region,每個(gè)Region占有一塊連續(xù)的虛擬內(nèi)存地址。如下圖所示:
在上圖中,我們注意到還有一些Region標(biāo)明了H,它代表Humongous,這表示這些Region存儲(chǔ)的是巨大對象(humongous object,H-obj),即大小大于等于region一半的對象。H-obj有如下幾個(gè)特征:
H-obj直接分配到了old gen (老年代),防止了反復(fù)拷貝移動(dòng)。
H-obj在global concurrent marking 階段的 cleanup 和 full GC 階段回收。
在分配H-obj之前先檢查是否超過 initiating heap occupancy percent和the marking threshold, 如果超過的話,就啟動(dòng) global concurrent marking,為的是提早回收,防止 evacuation failures 和 full GC。
為了減少連續(xù)H-objs分配對GC的影響,需要把大對象變?yōu)槠胀ǖ膶ο?,建議增大Region size。
二、SATB
全稱是Snapshot-At-The-Beginning,由字面理解,是GC開始時(shí)活著的對象的一個(gè)快照。它是通過Root Tracing得到的,作用是維持并發(fā)GC的正確性。
那么它是怎么維持并發(fā)GC的正確性的呢?根據(jù)三色標(biāo)記算法,我們知道對象存在三種狀態(tài):
白:對象沒有被標(biāo)記到,標(biāo)記階段結(jié)束后,會(huì)被當(dāng)做垃圾回收掉。
灰:對象被標(biāo)記了,但是它的field還沒有被標(biāo)記或標(biāo)記完。
黑:對象被標(biāo)記了,且它的所有field也被標(biāo)記完了。
由于并發(fā)階段的存在,Mutator(更改器和)Garbage Collector線程同時(shí)對對象進(jìn)行修改,就會(huì)出現(xiàn)白對象漏標(biāo)的情況,這種情況發(fā)生的前提是:
Mutator賦予一個(gè)黑對象該白對象的引用。
Mutator刪除了所有從灰對象到該白對象的直接或者間接引用。
對于第一個(gè)條件,在并發(fā)標(biāo)記階段,如果該白對象是new出來的,并沒有被灰對象持有,那么它會(huì)不會(huì)被漏標(biāo)呢?Region中有兩個(gè)top-at-mark-start(TAMS)指針,分別為prevTAMS和nextTAMS。在TAMS以上的對象是新分配的,這是一種隱式的標(biāo)記。
對于在GC時(shí)已經(jīng)存在的白對象,如果它是活著的,它必然會(huì)被另一個(gè)對象引用,即條件二中的灰對象。如果灰對象到白對象的直接引用或者間接引用被替換了,或者刪除了,白對象就會(huì)被漏標(biāo),從而導(dǎo)致被回收掉,這是非常嚴(yán)重的錯(cuò)誤,所以SATB破壞了第二個(gè)條件。
也就是說,一個(gè)對象的引用被替換時(shí),可以通過 write barrier 將舊引用記錄下來。(并沒有 看懂在說什么)
SATB也是有副作用的,如果被替換的白對象就是要被收集的垃圾,這次的標(biāo)記會(huì)讓它躲過GC,這就是float garbage。因?yàn)镾ATB的做法精度比較低,所以造成的float garbage也會(huì)比較多。
三、RSet
全稱是Remembered Set,是輔助GC過程的一種結(jié)構(gòu),典型的空間換時(shí)間工具,和Card Table有些類似。
還有一種數(shù)據(jù)結(jié)構(gòu)也是輔助GC的:Collection Set(CSet),它記錄了 GC要收集的Region集合 ,集合里的Region可以是任意年代的。
在GC的時(shí)候,對于old->young和old->old的跨代對象引用,只要掃描對應(yīng)的CSet中的RSet即可。
Rset : 屬于points-into結(jié)構(gòu)(誰引用了我的對象)
Card Table : 則是一種points-out(我引用了誰的對象)的結(jié)構(gòu)
G1的RSet是在Card Table的基礎(chǔ)上實(shí)現(xiàn)的:每個(gè)Region會(huì)記錄下別的Region有指向自己的指針,并標(biāo)記這些指針分別在哪些Card的范圍內(nèi)。
這個(gè)RSet其實(shí)是一個(gè)Hash Table,Key -- 別的Region的起始地址,Value是一個(gè)集合 -- 里面的元素是Card Table的Index。
這里解釋一下 :
上圖有三個(gè) Region 。紅色代表 Rset , 灰色大方框代表 Card Table。
Region2 的 Rset2 中有兩個(gè) Region 的起始地址,分別指向 Region1 , Region3。 -- 代表 Region1 與 Region3 引用了我的對象。
Region1 的 Card Table 位置上,存在一個(gè) 對 Region2 的引用。 -- 代表 Region1 引用了 Region2 的對象。
Region3 同理。
作用:
在做YGC(Minor GC)的時(shí)候,只需要選定young generation region的RSet作為根集,這些RSet記錄了old->young的跨代引用,避免了掃描整個(gè)old generation。
而mixed gc的時(shí)候,old generation中記錄了old->old的RSet,young->old的引用由掃描全部young generation region得到,這樣也不用掃描全部old generation region。所以RSet的引入大大減少了GC的工作量。
四、Pause Prediction Model
G1 uses a pause prediction model to meet a user-defined pause time target and selects the number of regions to collect based on the specified pause time target.
G1 GC是一個(gè)響應(yīng)時(shí)間優(yōu)先的GC算法,它與CMS最大的不同是,用戶可以設(shè)定整個(gè)GC過程的期望停頓時(shí)間,參數(shù)"-XX:MaxGCPauseMillis"指定一個(gè)G1收集過程目標(biāo)停頓時(shí)間,默認(rèn)值200ms。
G1 通過這個(gè)模型統(tǒng)計(jì)計(jì)算出來的歷史數(shù)據(jù)來預(yù)測本次收集需要選擇的Region數(shù)量,從而盡量滿足用戶設(shè)定的目標(biāo)停頓時(shí)間。
停頓預(yù)測模型是以衰減標(biāo)準(zhǔn)偏差為理論基礎(chǔ)實(shí)現(xiàn)的。
這里就不詳細(xì)介紹了,有興趣的,可以看 美團(tuán)大神的文章
程序計(jì)數(shù)器
虛擬機(jī)棧 : 局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口
本地方法棧 : native 方法
堆 : 所有的對象實(shí)例以及數(shù)組
方法區(qū) : 已被加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼
運(yùn)行時(shí)常量池 : 編譯期生成的各種字面量和符號引用
直接內(nèi)存 : NIO類引入了一種基于通道(channel) 與 緩沖區(qū)(buffer) 的 I/O 方式,使用 Native 函數(shù)庫直接分配堆外內(nèi)存 , 通過存儲(chǔ)在 Java 堆中的 DirectByteBuffer 對象作為這塊內(nèi)存的引用進(jìn)行操作。
1. Java 對象的內(nèi)存布局對象頭 : 哈希碼(2bit)-分代年齡(4)、輕量級鎖定(標(biāo)志位 00)、重量級鎖定、GC標(biāo)記、可偏向(標(biāo)志位 01),補(bǔ)充: 類型指針、數(shù)組長度
實(shí)例數(shù)據(jù) :
對齊填充
2. OOM 異常堆溢出: 不斷創(chuàng)建對象,并且存在可達(dá)路徑,不被清除。那么對象在達(dá)到最大堆容量限制后就會(huì)產(chǎn)生內(nèi)存溢出
通過 內(nèi)存映像分析工具 (Eclipse Memory Analyzer) 對 Dump 出來的堆轉(zhuǎn)存儲(chǔ)快照進(jìn)行分析。判斷是內(nèi)存泄漏還是內(nèi)存溢出。
虛擬機(jī)棧與本地方法棧溢出:
如果線程請求的棧深度大于虛擬機(jī)所允許的最大深度,將拋出 StackOverFlowError 異常。
如果虛擬機(jī)在擴(kuò)展棧時(shí)無法申請到足夠的內(nèi)存空間,則拋出 OutOfMemoryError 異常。
方法區(qū)
方法區(qū)存放 Class 的相關(guān)信息。如果存在大量的類 填滿 方法區(qū)。則會(huì)產(chǎn)生溢出。
通過 動(dòng)態(tài)代理 或 通過 CGLIB 動(dòng)態(tài)生成大量的類,以及大量 JSP與 動(dòng)態(tài)JSP 文件的應(yīng)用 。
3. OOM 異常的解決一. 可通過命令定期抓取heap dump或者啟動(dòng)參數(shù)OOM時(shí)自動(dòng)抓取heap dump文件。
二. 通過對比多個(gè)heap dump,以及heap dump的內(nèi)容,分析代碼找出內(nèi)存占用最多的地方。
三. 分析占用的內(nèi)存對象,是否是因?yàn)殄e(cuò)誤導(dǎo)致的內(nèi)存未及時(shí)釋放,或者數(shù)據(jù)過多導(dǎo)致的內(nèi)存溢出。
加載 :可以通過自定義類加載器參與
通過一個(gè)類的全限定名獲取定義此類的二進(jìn)制字節(jié)流
將這個(gè)字節(jié)流代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
在內(nèi)存中生成一個(gè)代表這個(gè)類的 java.lang.Class 對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口
驗(yàn)證
文件格式驗(yàn)證
元數(shù)據(jù)驗(yàn)證 : 語義校驗(yàn)
字節(jié)碼驗(yàn)證 :邏輯校驗(yàn)
符號引用驗(yàn)證 :發(fā)生在解析階段中,將符號引用轉(zhuǎn)化為直接引用
準(zhǔn)備 : 為類變量分配內(nèi)存并設(shè)置類變量初始值的階段
解析 : 將符號引用 替換 為直接引用的過程。
初始化 :
啟動(dòng)(Bootstrap)類加載器:采用 C++ 實(shí)現(xiàn),它負(fù)責(zé)將
擴(kuò)展(Extension)類加載器:擴(kuò)展類加載器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將< Java_Runtime_Home >/lib/ext或者由系統(tǒng)變量-Djava.ext.dir指定位置中的類庫加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。
系統(tǒng)(System)類加載器:系統(tǒng)類加載器是由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實(shí)現(xiàn)的。它負(fù)責(zé)將系統(tǒng)類路徑j(luò)ava -classpath或-Djava.class.path變量所指的目錄下的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。
工作過程:
如果一個(gè)類加載器收到了類的加載的請求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請求委派給父類加載器去完成。直到頂層的啟動(dòng)類加載器中,當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請求時(shí),子加載器會(huì)嘗試自己去加載。
方便 JNDI 服務(wù):SPI 的接口是 Java 核心庫的一部分,是由引導(dǎo)類加載器來加載的;SPI 實(shí)現(xiàn)的 Java 類一般是由系統(tǒng)類加載器來加載的。引導(dǎo)類加載器是無法找到 SPI 的實(shí)現(xiàn)類的,因?yàn)樗患虞d Java 的核心庫。它也不能代理給系統(tǒng)類加載器,因?yàn)樗窍到y(tǒng)類加載器的祖先類加載器。也就是說,類加載器的代理模式無法解決這個(gè)問題。
解決方法:Java 應(yīng)用的線程的上下文類加載器 默認(rèn) 就是系統(tǒng)上下文類加載器。在 SPI 接口的代碼中使用線程上下文類加載器,就可以成功的加載到 SPI 實(shí)現(xiàn)的類。線程上下文類加載器在很多 SPI 的實(shí)現(xiàn)中都會(huì)用到。
Java默認(rèn)的線程上下文類加載器是系統(tǒng)類加載器(AppClassLoader)。以下代碼摘自sun.misc.Launch的無參構(gòu)造函數(shù)Launch()。
可以通過 java.lang.Thread類 的 setContextClassLoader() 設(shè)置。
方便執(zhí)部署的實(shí)現(xiàn)。可以在不重啟服務(wù)器的情況下,對其中的邏輯代碼進(jìn)行更新。
由 父類加載器 與 Bundle 組成 , 每個(gè) Bundle 的功能都是 發(fā)布 export 與依賴 import。從而形成復(fù)雜的網(wǎng)狀結(jié)構(gòu)
原理:
OSGi 中的每個(gè)模塊都有對應(yīng)的一個(gè)類加載器。它負(fù)責(zé)加載模塊自己包含的 Java 包和類。
當(dāng)它需要加載 Java 核心庫的類時(shí)(以 java開頭的包和類),它會(huì)代理給父類加載器(通常是啟動(dòng)類加載器)來完成。
當(dāng)它需要加載所導(dǎo)入的 Java 類時(shí),它會(huì)代理給導(dǎo)出此 Java 類的模塊來完成加載。
在雙親委派模型的基礎(chǔ)上加入了 Common類加載器,Catalina類加載器,Shared類加載器,WebApp類加載器,Jsp類加載器
Common類加載器, /common 目錄 被 Tomcat 與 所以 Web 應(yīng)用程序共同使用
Catalina類加載器, /server 目錄中, 被 Tomcat 使用
Shared類加載器, /shared 目錄中 ,被所有 Web 應(yīng)用程序共同使用
WebApp類加載器,Jsp類加載器 , /WebApp/WEB-INF 目錄中,只能被此 Web 應(yīng)用程序使用。
Mixed Mode -- 混合模式
默認(rèn)為混合模式,解釋器與編譯器搭配使用。
Interpreted Mode -- 解釋模式
使用 “-Xint" 參數(shù)。只使用解釋。
Compiled Mode -- 編譯模式
使用 “-Xcomp" 參數(shù)。 優(yōu)先采用編譯,當(dāng)編譯無法進(jìn)行時(shí),使用解釋。
-version 命令,可以輸出顯示這三種模式
2. 分層編譯(Tiered Compilation)JDK1.7 中的 Server 模式虛擬機(jī)中被作為默認(rèn)編譯策略。
0層,程序解釋執(zhí)行,解釋器不開啟性能監(jiān)控功能(Profiling),可觸發(fā)第一層編譯
1層,也叫C1 編譯(下文有解釋),將字節(jié)碼編譯為本地代碼,進(jìn)行簡單、可靠的優(yōu)化
2層,C2編譯。
3. OSR編譯因?yàn)榇嬖诙啻螆?zhí)行的循環(huán)體,所以觸發(fā) OSR 編譯,以整個(gè)方法 作為編譯對象。
發(fā)生在方法執(zhí)行過程中,所以叫( On Stack Replacement ) 方法棧幀還在棧上,方法就被替換了。
熱點(diǎn)代碼的分類:
被多次調(diào)用的方法
被多次執(zhí)行的方法體 -- OSR 編譯
熱點(diǎn)探測(Hot Spot Detection)
基于采樣 : 如果周期性的檢查各個(gè)線程的棧頂,如果發(fā)現(xiàn)某個(gè)方法經(jīng)常出現(xiàn)在棧頂,則這個(gè)方法就是“熱點(diǎn)方法”。
基于計(jì)數(shù)器 -- HotSpot 虛擬機(jī)中采用。
原理: 為每個(gè)方法建立計(jì)數(shù)器,統(tǒng)計(jì)方法的次數(shù),如果執(zhí)行次數(shù)超過一定的閾值,就認(rèn)為它是“熱點(diǎn)方法”
計(jì)數(shù)器分類:
方法調(diào)用計(jì)數(shù)器(Invocation Counter) :
**統(tǒng)計(jì)一段時(shí)間內(nèi)**,方法被調(diào)用的次數(shù),如果超過時(shí)間限度,則將這個(gè)方法的調(diào)用計(jì)數(shù)器減少一半,稱為**衰減**
回邊計(jì)數(shù)器(Back Edge Counter) : 統(tǒng)計(jì)一個(gè)方法中循環(huán)體被執(zhí)行的次數(shù) -- OSR 編譯
在字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令,稱為回邊。5. 優(yōu)化措施
hotspot中內(nèi)嵌有2個(gè)JIT編譯器,分別為Client Compiler,Server Compiler,但大多數(shù)情況下我們稱之為C1編譯器和C2編譯器。
client compiler,又稱C1編譯器,較為輕量,只做少量性能開銷比較高的優(yōu)化,它占用內(nèi)存較少,適合于桌面交互式應(yīng)用。
在寄存器分配策略上,JDK6以后采用的為線性掃描寄存器分配算法,其他方面的優(yōu)化,主要有方法內(nèi)聯(lián)、去虛擬化、冗余消除等。
A、方法內(nèi)聯(lián)
多個(gè)方法調(diào)用,執(zhí)行時(shí)要經(jīng)歷多次參數(shù)傳遞,返回值傳遞及跳轉(zhuǎn)等,C1采用方法內(nèi)聯(lián),把調(diào)用到的方法的指令直接植入當(dāng)前方法中。-XX:+PringInlining來查看方法內(nèi)聯(lián)信息,-XX:MaxInlineSize=35控制編譯后文件大小。
B、去虛擬化
是指在裝載class文件后,進(jìn)行類層次的分析,如果發(fā)現(xiàn)類中的方法只提供一個(gè)實(shí)現(xiàn)類,那么對于調(diào)用了此方法的代碼,也可以進(jìn)行方法內(nèi)聯(lián),從而提升執(zhí)行的性能。
C、冗余消除
在編譯時(shí)根據(jù)運(yùn)行時(shí)狀況進(jìn)行代碼折疊或消除。
Server compiler,稱為C2編譯器,較為重量,采用了大量傳統(tǒng)編譯優(yōu)化的技巧來進(jìn)行優(yōu)化,占用內(nèi)存相對多一些,適合服務(wù)器端的應(yīng)用。和C1的不同主要在于寄存器分配策略及優(yōu)化范圍.
寄存器分配策略上C2采用的為傳統(tǒng)的圖著色寄存器分配算法,由于C2會(huì)收集程序運(yùn)行信息,因此其優(yōu)化范圍更多在于全局優(yōu)化,不僅僅是一個(gè)方塊的優(yōu)化。
收集的信息主要有:分支的跳轉(zhuǎn)/不跳轉(zhuǎn)的頻率、某條指令上出現(xiàn)過的類型、是否出現(xiàn)過空值、是否出現(xiàn)過異常等。
逃逸分析(Escape Analysis) 是C2進(jìn)行很多優(yōu)化的基礎(chǔ),它根據(jù)運(yùn)行狀態(tài)來判斷方法中的變量是否會(huì)被外部讀取,如不會(huì)則認(rèn)為此變量是不會(huì)逃逸的,那么在編譯時(shí)會(huì)做標(biāo)量替換、棧上分配和同步消除等優(yōu)化。
如果證明一個(gè)對象不會(huì)逃逸到方法或線程之外,則:
- 棧上分配(Stack Allocation) :確定不會(huì)逃逸到**方法外**,讓這個(gè)對象在棧上分配內(nèi)存,對象占用的內(nèi)存空間可以隨棧幀的出棧而銷毀。 - 同步消除(Synchronization Elimination) :確定不會(huì)逃逸到**線程外**,則無法被其他線程訪問,所以可以取消同步措施。 - 標(biāo)量替換(Scalar Repalcement) : 標(biāo)量(Scalar)指一個(gè)數(shù)據(jù)無法再分解成更小的數(shù)據(jù)來表示 -- Java 中的原始數(shù)據(jù)類型 聚合量(Aggregate)指一個(gè)數(shù)據(jù)可以繼續(xù)分解 -- Java 中的對象 **原理:**直接創(chuàng)建若干個(gè)可以被方法使用的成員變量來替代。
語言無關(guān)的經(jīng)典優(yōu)化技術(shù) -- 公共子表達(dá)式消除(Common Subexpression Elimination)
如果一個(gè)表達(dá)式E 已經(jīng)計(jì)算過,并且從先前的計(jì)算 到現(xiàn)在 值未曾改變,那么如果 E 再次出現(xiàn),則可以直接使用之前的表達(dá)式結(jié)果,代替 E 。
語言相關(guān)的經(jīng)典優(yōu)化技術(shù) -- 數(shù)組邊界檢查消除(Array Bounds Checking Elimination)
這個(gè)不是很了解,做一個(gè)重點(diǎn)。。。 以后整理
靜態(tài)分派 : 依靠靜態(tài)類型 定位方法。
編譯階段:Human man = new Man(); // 靜態(tài)類型為 Human
運(yùn)行階段:man.sayHello() // 動(dòng)態(tài)類型為 Man
重載的優(yōu)先級
sayHello(char arg);
char -> int -> long -> float -> double // 不可轉(zhuǎn)化為 byte short , 因?yàn)閏har 轉(zhuǎn)化是不安全的。
-> Character -> Serializable/Comparable -> Object -> char...(變長參數(shù))
宗量:方法的接收者與方法的參數(shù)統(tǒng)稱為宗量
單分派 根據(jù)一個(gè)宗量對目標(biāo)方法進(jìn)行選擇
多分派 根據(jù)多個(gè)宗量對目標(biāo)方法進(jìn)行選擇
public class QQ{}; public class _360{}; public static class Father { public void hardChoice(QQ arg); public void hardChoice(_360 arg); } public static class Son extends Father{ public void hardChoice(QQ arg); public void hardChoice(_360 arg); } Father father = new Father(); Father son = new Son(); // 靜態(tài)多分派 - 編譯 : 方法的接收者 Father - Son, 參數(shù) QQ - _360 father.hardChoice(_360); // 動(dòng)態(tài)多分派 - 運(yùn)行 : 已經(jīng)確定 參數(shù)為 QQ ,再判斷 實(shí)際類型 , son的實(shí)際類型為 Son 。 son.hardChoice(QQ);結(jié)語
都看到這里了,點(diǎn)個(gè)關(guān)注,點(diǎn)波贊再走,QAQ。
你的小手輕點(diǎn),是我最大的動(dòng)力哦。
一只想當(dāng)程序員的1米88處女座大可愛如此說道。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67768.html
摘要:曾經(jīng)的小學(xué)生,初中生現(xiàn)如今已是社會(huì)的中流砥柱。一個(gè)互聯(lián)網(wǎng)公司要正常運(yùn)作,必定就需要一群足智多謀,思維敏捷,邏輯縝密的程序猿。這群能力超凡的人類,其實(shí)也有不堪一擊的致命弱點(diǎn)。同時(shí)我們也該擁有同等的尊重。 時(shí)光荏苒,歲月如梭。曾經(jīng)的小學(xué)生,初中生現(xiàn)如今已是社會(huì)的中流砥柱。隨著科技時(shí)代的迅猛發(fā)展,曾經(jīng)的荒土,已是星羅棋布高樓聳立。其中不乏科技相關(guān)的樓宇,俗稱互聯(lián)網(wǎng)公司。也許你,現(xiàn)在就正處于其...
摘要:曾經(jīng)的小學(xué)生,初中生現(xiàn)如今已是社會(huì)的中流砥柱。一個(gè)互聯(lián)網(wǎng)公司要正常運(yùn)作,必定就需要一群足智多謀,思維敏捷,邏輯縝密的程序猿。這群能力超凡的人類,其實(shí)也有不堪一擊的致命弱點(diǎn)。同時(shí)我們也該擁有同等的尊重。 時(shí)光荏苒,歲月如梭。曾經(jīng)的小學(xué)生,初中生現(xiàn)如今已是社會(huì)的中流砥柱。隨著科技時(shí)代的迅猛發(fā)展,曾經(jīng)的荒土,已是星羅棋布高樓聳立。其中不乏科技相關(guān)的樓宇,俗稱互聯(lián)網(wǎng)公司。也許你,現(xiàn)在就正處于其...
摘要:前端日報(bào)精選譯發(fā)布了王躍關(guān)于微信小程序的技術(shù),也許你想錯(cuò)了細(xì)說中的瀏覽器頁面渲染工作原理淺析騰訊前端團(tuán)隊(duì)社區(qū)中文第期安息吧,長存譯借助函數(shù)完成可組合的數(shù)據(jù)類型軟件編寫第十部分掘金對象與原型掘金技術(shù)周刊期知乎專欄是真正的語言 2017-10-16 前端日報(bào) 精選 [譯]Vue 2.5 發(fā)布了王躍:關(guān)于微信小程序的技術(shù),也許你想錯(cuò)了細(xì)說Web API中的Blobchrome瀏覽器頁面渲染工...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)。因?yàn)槲倚睦砗芮宄?,我的目?biāo)是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學(xué)習(xí)計(jì)劃,將我的短期目標(biāo)更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實(shí)習(xí)春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實(shí)習(xí)offer。然后五月懷著忐忑的心情開始了螞蟻金...
閱讀 733·2023-04-25 20:32
閱讀 2297·2021-11-24 10:27
閱讀 4538·2021-09-29 09:47
閱讀 2252·2021-09-28 09:36
閱讀 3655·2021-09-22 15:27
閱讀 2774·2019-08-30 15:54
閱讀 382·2019-08-30 11:06
閱讀 1280·2019-08-30 10:58