<<深入理解JVM>>筆記與一些體悟


這段時間這本書的第二遍已經(jīng)看完了打瘪,但是很多地方模糊不清楚移剪,總的來說拔妥,這邊書收獲很大舍咖,作為Java開發(fā),這本書很有幫助铣卡,本來準備進軍多線程的链韭,因此而耽擱一段時間,總之感覺還是把這本書一些章節(jié)吃透煮落,再去多線程

第一章 走進Java(歷史與展望)

展望Java技術(shù)的未來:從目前國內(nèi)形勢來看敞峭,go-lang在逐漸走上臺面,php也同時占有一席之地
PYPL排行榜也是一個關(guān)于編程語言流行度的參考指標州邢,其榜單數(shù)據(jù)的排名均是根據(jù)榜單對象在 Google 上相關(guān)的搜索頻率進行統(tǒng)計排名儡陨,原始數(shù)據(jù)來自 Google Trends,也就是說某項語言或者某款 IDE 在 Google 上搜索頻率越高量淌,表示它越受歡迎骗村。上面這份排行是基于google搜索次數(shù)決定的



TIOBE編程社區(qū)索引是編程語言流行程度的一個指標。索引每月更新一次呀枢。評級是基于全球熟練工程師胚股、課程和第三方供應(yīng)商的數(shù)量。流行的搜索引擎裙秋,如谷歌琅拌,必應(yīng),雅虎摘刑!进宝,維基百科,亞馬遜枷恕,YouTube和百度被用來計算收視率党晋。需要注意的是,TIOBE索引并不是關(guān)于最好的編程語言徐块,也不是大多數(shù)代碼都是用哪種語言編寫的未玻。索引可用于檢查您的編程技能是否仍然是最新的,或者在開始構(gòu)建新的軟件系統(tǒng)時胡控,對應(yīng)采用何種編程語言作出戰(zhàn)略決策

Java從當(dāng)初的一次編寫到處運行 扳剿,到未來的期望無語言傾向,感覺面臨著巨大的挑戰(zhàn)昼激,畢竟現(xiàn)在還是天下第一
Java的優(yōu)勢:
1.龐大的用戶群體庇绽。
2.穩(wěn)定的語言锡搜,使得項目更正規(guī),更容易形成大型體系的工程瞧掺。
缺點:
1.泛型那里很不好用余爆,Java采用了類型擦除方式。使得使用泛型會造成大量的自動拆箱夸盟、裝箱,使得泛型速度變慢像捶。
2.啟動Java虛擬機的時候上陕,還是太長了,不如一些動態(tài)類型語言來的開發(fā)效率高拓春,對編程人員释簿,用戶也友好。

正式進入本書:
一硼莽、無語言傾向
2018年4月庶溶,Oracle Labs新公開了一項黑科技:Graal VM,這是一個在Hotspot虛擬機基礎(chǔ)上增強而成的跨語言全棧虛擬機懂鸵,可以作為“任何語言”的運行平臺偏螺,既包括Java、Scala匆光、Groovy等基于Java虛擬機的語言套像,還包括C、C++终息、Rust等基于LLVM的語言夺巩,同時也支持,Javascript周崭、Python和R語言等柳譬。Graal VM 可以無額外開銷地混合使用這些編程語言,支持不同語言中混用對方的接口和對象续镇,也能夠支持這些語言使用已經(jīng)編寫好地本地庫文件美澳。它的基本工作原理:將這些語言的源代碼或者源代碼編譯后的中間格式(例如LLVM字節(jié)碼)通過解釋器轉(zhuǎn)換為能被Graal VM接受的中間表示,例如設(shè)計一個解釋器專門對LLVM輸出的字節(jié)碼進行轉(zhuǎn)換來支持C語言磨取。(Truffle快速構(gòu)建面向一種新語言的解釋器人柿。)Graal VM才是真正意義上的與物理計算機相對應(yīng)的高級語言虛擬機,理由是它與物理硬件的指令集一樣忙厌,做到了只與機器特性相關(guān)而不與某種高級語言特性相關(guān)凫岖。
Graal VM相比于Hotspot 主要差異在于即時編譯器,相比較起來互有勝負逢净,但是Oracle Labs和美國大學(xué)里所做的最新即時編譯技術(shù)的研究全部都遷移到基于Graal VM之上進行了哥放,令人期待歼指。

二、新一代即時編譯器
Hotspot 虛擬機中含有兩個即時編譯器甥雕,分別是編譯耗時短但是輸出代碼優(yōu)化程度較低的客戶端編譯器(C1)以及編譯耗時長但輸出代碼優(yōu)化質(zhì)量也更高的服務(wù)端編譯器(C2)踩身,通常他們會在分層編譯機制下與解釋器互相配合來共同構(gòu)成Hotspot虛擬機的執(zhí)行子系統(tǒng)。
Graal 編譯器(作為C2編譯器替代者)社露,C2時間太長了挟阻,其作者都因為太復(fù)雜而不愿意維護,且用C++編寫而成峭弟。Graal編譯器本身就是Java語言寫成附鸽,且C2編譯器代碼可以輕松移植到Graal編譯器上,不成熟瞒瘸,需要通過-XX:+UnlockExperimentalVMOptions -XX:UseJVMCICompiler參數(shù)來開啟坷备。

三、向native邁進
Java自身存在缺點情臭,主要是近幾年在從大型單體應(yīng)用架構(gòu)向小型微服務(wù)架構(gòu)發(fā)展的技術(shù)潮流之下省撑,Java表現(xiàn)的不適應(yīng)。(沒看過微服務(wù)所以詳細寫一下)在微服務(wù)架構(gòu)視角下俯在,應(yīng)用拆分后竟秫,單個微服務(wù)不在需要面隊數(shù)十、數(shù)百GB乃至TB的內(nèi)存跷乐,有了高可用的服務(wù)集群鸿摇,也無需追求單個服務(wù)7*24小時運行,隨時中斷和更新劈猿;但是Java的啟動時間較長拙吉,需要時間才能到達最高性能,就和這些場景有點矛盾揪荣。在無服務(wù)架構(gòu)下筷黔,矛盾會更大。
AppCDS 允許把加載解析的類型信息緩存起來仗颈,從而提升下次啟動速度佛舱。提前編譯能帶來的最大好處是Java虛擬機加載這些預(yù)編譯成二進制庫之后能直接調(diào)用,無需等待即時編譯器在運行是將其編譯成二進制機器碼挨决,理論上请祖,提前編譯可以減少即時編譯帶來的預(yù)熱時間,減少Java長期給人帶來的第一次運行慢的不良體驗脖祈。但是壞處也很明顯肆捕,必須為不同的硬件、操作系統(tǒng)去編譯對應(yīng)的發(fā)行包盖高;降低Java連接過程的動態(tài)性慎陵,必須要求加載的代碼在編譯期全部已知眼虱,而不能在運行期才確定否則只能舍棄以及編譯好的,退回即時編譯狀態(tài)席纽。
SubStrate VM出現(xiàn)捏悬,一個極小的運行時環(huán)境,包括了獨立的異常處理润梯、同步調(diào)度过牙、線程管理、內(nèi)存管理和JNI訪問纺铭,目標是代替Hotspot用來支持提前編譯后的程序運行抒和,無需重復(fù)開啟Java虛擬機初始化過程,不能動態(tài)加載其他編譯器不可知的代碼和類庫彤蔽。好處就是顯著降低內(nèi)存占用和啟動時間,運行在Substrate VM上的小規(guī)模應(yīng)用庙洼,其內(nèi)存占用和啟動時間比Hotspot下降5-50倍顿痪。

四、Java語法糖持續(xù)變多油够,給編程人員提供良好的體驗
結(jié)束~
第一部分 自動內(nèi)存管理 援引作者一句話Java與C++之間由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的高墻蚁袭,墻外面的人想進去,墻里面的人卻想出來

第二章 Java內(nèi)存區(qū)域與內(nèi)存溢出異常

1. 運行時數(shù)據(jù)區(qū)域
共包含:方法區(qū)(線程公有)石咬、Java堆(線程公有)揩悄、Java虛擬機棧(線程私有)、本地方法棧(線程私有)鬼悠、程序計數(shù)器(線程私有)删性、運行時常量池(方法區(qū)一部分)、直接內(nèi)存(不是虛擬機運行時數(shù)據(jù)區(qū)的一部分焕窝,也不是《Java虛擬機規(guī)范》中定義的區(qū)域蹬挺。
1.1 程序計數(shù)器
1.字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條執(zhí)行的字節(jié)碼指令,是程序控制流的指示器它掂,線程恢復(fù)也需要依賴程序計數(shù)器巴帮。
2.Java虛擬機多線程通過線程切換、分配處理器執(zhí)行時間的方式實現(xiàn)虐秋,一個確定的時刻一個處理器只會執(zhí)行一條線程中的指令榕茧,因此每個線程各自擁有自己的程序計數(shù)器
3.如果執(zhí)行Java方法則計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址,如果執(zhí)行的是native方法客给,則計數(shù)器值為空
1.2 虛擬機棧
1.其線程私有用押、它的生命周期與線程相同;每個方法被執(zhí)行時靶剑,Java虛擬機都會創(chuàng)建一個棧幀用于存儲局部變量表只恨、操作數(shù)棧译仗、動態(tài)鏈接、方法出口等信息官觅。每一個方法被調(diào)用直至執(zhí)行完畢纵菌,就對應(yīng)一個棧幀在虛擬機棧中從入棧到出棧的過程。
2.Java內(nèi)存區(qū)域休涤,程序員最關(guān)心兩個區(qū)域堆(heap)和棧(stack)咱圆,棧指的是虛擬機棧或者虛擬機棧中的局部變量表部分功氨。
3.局部變量表存放了基本數(shù)據(jù)類型(boolean序苏、byte、char捷凄、short忱详、int、float跺涤、long匈睁、double)、引用類型(reference)和returnAddress類型桶错。
4.局部變量表的存儲空間以局部變量槽slot表示航唆,long 和 double都占用2個slot,其余占用一個院刁,局部變量表所需的內(nèi)存空間在編譯期就完成糯钙,在方法的運行時期不會改變局部變量表大小
5.如果線程請求的棧深度大于虛擬機所允許的深度,拋出StackOverFlow異常退腥;如果Java虛擬機棧容量可以動態(tài)擴展任岸、當(dāng)棧擴展時無法申請到足夠的內(nèi)存會拋出OutOfMemory異常。
1.3 本地方法棧
1.和虛擬機棧發(fā)揮的作用類似狡刘,區(qū)別就是虛擬機棧為執(zhí)行Java方法服務(wù)演闭,本地方法棧則為虛擬機棧用到的Native方法服務(wù)。
2.異常類同虛擬機棧
1.4 Java堆
1.堆中只存儲對象實例颓帝。
2.Java是垃圾收集器管理的內(nèi)存區(qū)域米碰,從分配內(nèi)存的角度看,所有線程共享的Java堆可以劃分出多個線程私有的分配緩沖區(qū)购城,用來提升對象分配效率吕座。Java堆的劃分只有一個目的就是,更好的分配內(nèi)存瘪板,更好的回收內(nèi)存 吴趴。
3.堆里面不要求物理上連續(xù)存儲,但邏輯上是連續(xù)的侮攀,對于大對象(典型的數(shù)組對象)很可能要求連續(xù)的內(nèi)存空間锣枝。
4.如果在Java堆中沒有內(nèi)存完成實例分配厢拭,并且堆無法再擴展時,拋出OutOfMemoryError(OOM)撇叁。
1.5 方法區(qū)
1.用于存儲已經(jīng)被虛擬機加載類型信息供鸠、常量、靜態(tài)變量陨闹、即時編譯器編譯后的代碼緩存等數(shù)據(jù)楞捂。
2.JDK8完全廢除永久代,采用本地內(nèi)存來實現(xiàn)方法區(qū)趋厉,使用元空間寨闹。
3.這個區(qū)域內(nèi)存回收的目標是:常量池的回收和對類型的卸載。(比較難實現(xiàn))
4.如果方法區(qū)無法滿足新的內(nèi)存分配需求時君账,拋出OOM繁堡。
1.5 運行時常量池
1.作為方法區(qū)的一部分,常量池表乡数,用于存放編譯期生成的各種字面量與符號引用椭蹄,這部分內(nèi)容將在類加載后放在運行時常量池中⊥В可能也會存儲直接引用
2.具備動態(tài)性,Java并不要求常量一定要編譯期才可以產(chǎn)生例如String類的intern()方法澈侠。
3.當(dāng)常量池?zé)o法申請到內(nèi)存時拋出OOM
1.6 直接內(nèi)存
1.不是虛擬機運行時數(shù)據(jù)區(qū)的一部分劫侧,也不是《Java虛擬機規(guī)范》中定義的區(qū)域。
2.JDK1.4 新加入的NIO哨啃,引入了一種基于通道channel與緩沖區(qū)BUffer的I/O方式烧栋,使用Native函數(shù)直接分配堆外內(nèi)存,通過存儲在Java堆里面的DirectByteBUffer對象作為這塊區(qū)域的引用進行操作拳球,避免了Java堆和Native堆中來回復(fù)制數(shù)據(jù)审姓。
3.本機直接內(nèi)存分配不受到Java堆大小的限制,但收到總內(nèi)存限制祝峻,一般服務(wù)器管理員配置虛擬機參數(shù)時魔吐,忽略直接內(nèi)存,使得各內(nèi)存區(qū)域總和大于物理內(nèi)存限制莱找,導(dǎo)致OOM異常酬姆。
2. 對象的一生
Hotspot虛擬機在Java堆中對象分配、布局和訪問的全過程奥溺。
2.1 對象的創(chuàng)建
1.當(dāng)虛擬機遇到一條字節(jié)碼new指令時辞色,首先去檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查到這個符號引用代表的類是否已經(jīng)被加載浮定、解析和初始化過相满,如果沒有先執(zhí)行相應(yīng)的類加載层亿。
2.類加載檢查通過后,給對象分配內(nèi)存:
(1)指針碰撞:假設(shè)Java堆中內(nèi)存是絕對規(guī)整的立美,所有被用過的內(nèi)存放一邊匿又,空閑的內(nèi)存放一邊,中間放一個指針作為分界點的指示器悯辙,那所分配內(nèi)存就是僅僅把那個指針向空閑空間方向挪動一段與對象大小相等的距離琳省。
(2)空閑列表:如果不規(guī)整,就必須維護一個空閑列表躲撰,記錄哪塊內(nèi)存可以用针贬,分配的時候從列表中找到足夠大的一塊空間分給對象實例,并更新記錄拢蛋。
選擇哪種方式由Java堆是否規(guī)整來決定桦他,而Java堆是否規(guī)整,又由所采用的垃圾收集器是否帶有空間壓縮整理決定谆棱。因此快压,使用Serial、Parnew等帶壓縮整理的收集器垃瞧,采用指針碰撞蔫劣;采用CMS基于清楚算法的收集器時,理論上采用空閑列表實現(xiàn)个从。
還需要考慮一個問題脉幢,對象創(chuàng)建是很頻繁的行為,僅僅修改一個指針所指向的位置嗦锐,并發(fā)情況下并不是線程安全的嫌松,可能出現(xiàn)給A分配內(nèi)存、指針沒修改奕污,對象B又使用原來指針分配內(nèi)存萎羔,解決方法:
(1)對分配內(nèi)存空間進行同步處理——實際上虛擬機采用CAS配上失敗重試的方式保證操作的原子性
CAS看這里CAS
(2)另外一種是把內(nèi)存分配的動作按照線程劃分在不同空間中進行,即每個線程在Java堆中預(yù)先分配一小塊內(nèi)存碳默,稱為本地線程分配緩沖(TLAB)贾陷,哪個線程要分配內(nèi)存,就在哪個線程的本地緩沖區(qū)分配嘱根,只有本地緩沖區(qū)用完了昵宇,分配新的緩沖區(qū)才需要同步鎖定。
3.內(nèi)存分配完成后儿子,虛擬機將分配到的內(nèi)存空間(但不包括對象頭)都初始化為0瓦哎,這步操作保證了對象的實例字段在Java代碼中不賦初值就可以使用,使程序能訪問到這些字段的數(shù)據(jù)類型所對應(yīng)的零值。
4.接下來蒋譬,對對象進行必要的設(shè)置割岛,例如這個對象是哪個類的實例,犯助、如何才能找到類的元數(shù)據(jù)信息癣漆、對象的哈希碼、對象的GC年齡分帶剂买,存儲在對象頭之中
5.構(gòu)造函數(shù)惠爽,即CLass中的<init>()方法,按照程序員自己的意愿進行初始化瞬哼,這樣一個對象才被完整構(gòu)建出來
2.2 對象的內(nèi)存布局
1.對象在堆內(nèi)存中可以劃分為三個部分:對象頭婚肆、實例數(shù)據(jù)、對齊填充
2.對象頭:MarkWord 如:哈希碼坐慰、GC年齡分帶较性、鎖狀態(tài)標志、線程持有的鎖结胀、偏向線程ID等赞咙,另外一部分就是類型指針,即對象指向它的類型數(shù)據(jù)的指針糟港,虛擬機需要通過這個指針來確定該對象是哪個類的實例攀操。如果對象是Java數(shù)組,還要存儲一塊這個對象多大秸抚,記錄長度速和。數(shù)組大小不確定,虛擬機無法通過元數(shù)據(jù)確定數(shù)組大小耸别。
3.實例數(shù)據(jù):是對象真正存儲的有效信息
4.對齊填充:因為Hotspot要求對象起始地址必須是8字節(jié)的整數(shù)倍健芭,換句話說就是任何對象大小必須是8字節(jié)的整數(shù)倍县钥,所以需要對齊填充秀姐。
2.3 對象的訪問定位
1.主流方式使用句柄和直接指針兩種
(1)使用句柄的化,Java堆中可能劃分一塊內(nèi)存作為句柄池若贮,reference中存儲的就是對象的句柄地址省有,而句柄中包含了對象實例數(shù)據(jù)與類型實例數(shù)據(jù)各自具體的地址信息。
優(yōu)勢:reference中存儲的是穩(wěn)定的句柄地址谴麦,在對象移動(垃圾收集)的時候蠢沿,只會改變句柄中實例數(shù)據(jù)指針,而reference本身不要修改匾效。
(2)使用直接指針的化舷蟀,Java堆中對象的內(nèi)存布局就必須要考慮如何放置訪問類型數(shù)據(jù)的相關(guān)信息,reference中存儲的直接就是對象地址,如果訪問對象本身的化野宜,就不需多一次間接訪問的開銷扫步。
優(yōu)勢:速度快,節(jié)省一次指針定位的時間開銷匈子,(Hotspot)中就用直接指針河胎。

3. 實戰(zhàn):OutOfMemoryError異常

第三章 垃圾收集器與內(nèi)存分配策略

1)垃圾回收出現(xiàn)的原因:前面一章,分析了虎敦,JVM哪些地方會出現(xiàn)OOM異常游岳,這章介紹Java垃圾收集器為了避免內(nèi)存溢出異常都做了哪些努力。
2)為什么學(xué)習(xí)垃圾回收其徙?當(dāng)排查各種內(nèi)存溢出胚迫、內(nèi)存泄漏問題時,當(dāng)垃圾收集成為系統(tǒng)達到更高并發(fā)量的瓶頸時擂橘,我們就必須對這些“自動化技術(shù)”的技術(shù)實施必要的監(jiān)控和調(diào)節(jié)晌区。
3)哪些內(nèi)存需要回收?程序計數(shù)器通贞、虛擬機棧朗若、本地方法棧隨線程而生,隨線程消滅昌罩,棧中的棧幀隨著方法的進入和退出有條不紊的執(zhí)行著入棧和出棧操作哭懈。并且每一個棧幀分配多少內(nèi)存是確定下來的,大體上是編譯器已知的茎用,因此這幾個區(qū)域回收都具有確定性遣总,當(dāng)方法結(jié)束或者線程結(jié)束就不需要考慮太多問題。所以回收主要面向Java堆方法區(qū)轨功。

  1. 對象已死旭斥?
    1)引用計數(shù)法:對象中添加一個引用計數(shù)器,如果引用+1古涧,引用失效-1垂券,任何時刻引用為零的對象就是不可能再被使用的。但是有些問題無法解決羡滑,例如循環(huán)引用問題菇爪。
    2)可達性分析算法:用GC Roots作為根對象,根據(jù)引用關(guān)系向下搜索柒昏,對象不可達凳宙,則對象不在使用。固定作為GC Roots的對象包括以下幾種职祷,
    1.在虛擬機棧(棧幀中的本地變量表)中引用的對象氏涩,比如各個線程被調(diào)用的方法堆棧中用到的參數(shù)届囚、局部變量、臨時變量是尖。
    2.在方法區(qū)中類靜態(tài)屬性引用的對象奖亚,比如Java類的引用類型靜態(tài)變量。
    3.方法區(qū)中常量引用的對象析砸。
    4.本地方法棧中JNI引用的對象昔字。
    5.基本數(shù)據(jù)類型對應(yīng)的Class對象,系統(tǒng)類加載器首繁。
    6.所有被同步鎖(synchronized關(guān)鍵字)持有的對象作郭。
    7.反應(yīng)Java虛擬機內(nèi)部情況地JMXBean、JVMTI中注冊的回調(diào)弦疮、本地代碼緩存等夹攒。
    備注:可能會有臨時性加入,做局部回收的時候胁塞,某個區(qū)域內(nèi)的對象完全有可能被位于堆中的其他區(qū)域引用咏尝,這時就需要將這些關(guān)聯(lián)區(qū)域?qū)ο笠徊⒓尤隚C Roots集合中去,才能保證可達性分析的正確啸罢。
    3)四種引用關(guān)系的出現(xiàn)
    出現(xiàn)原因:當(dāng)內(nèi)存空間還足夠時编检,能保留在內(nèi)存中,如果內(nèi)存空間再進行垃圾收集后仍然非常緊張扰才,那就可以拋棄這些對象——系統(tǒng)緩存
    1.強引用:Object obj = new Object() 這種引用關(guān)系允懂。只要強引用在,垃圾收集器就永遠不會回收掉被引用對象衩匣。
    2.軟引用:軟引用用來描述一些還有用蕾总、非必須的對象,只要被軟引用關(guān)聯(lián)著的對象琅捏,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常前生百,會把這些對象列進回收范圍進行第二次回收发侵,SoftReference
    3.弱引用:用來描述那些非必須的對象粮揉,但是它強度比軟引用更弱一點沮尿,被弱引用關(guān)聯(lián)的對象只能存活到下一次垃圾收集之前彼城,垃圾收集器開始工作,都會回收围辙,WeakReference
    4.虛引用:一個對象是否有虛引用,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來取得一個對象實例赎败,唯一目的就是:為了能在這個對象被收集器回收時收到一個系統(tǒng)通知,PhantomReference蠢甲。
    4)回收方法區(qū)
    回收內(nèi)容:廢棄的常量和不再使用的類型
    判讀類型是否不再被使用(同時滿足以下條件):
    1.該類中所有實例都被回收
    2.加載該類的類加載器已經(jīng)被回收僵刮,除非是精心設(shè)計過的可替換的類加載器,如OSGI,JSP等搞糕,否則很難達成
    3.該類對應(yīng)的對象沒有在任何地方被引用過勇吊,無法在任何地方通過反射訪問該類
    備注:在大量使用反射、動態(tài)代理窍仰、CGLib等字節(jié)碼框架汉规,動態(tài)生成JSP以及OSGI這類頻繁自定義類加載器的場景中,通常都需要具備類型卸載的能力驹吮,以保證不會造成過大的內(nèi)存壓力针史。
  2. 垃圾收集算法
    首先分為:引用計數(shù)式垃圾收集和追蹤式垃圾收集也被稱為直接垃圾收集和簡接垃圾收集,Java主要用追蹤式垃圾收集碟狞。
    1)分代收集理論
    弱分代假說:絕大多數(shù)對象都是朝生夕滅的啄枕。
    強分代假說:熬過越多次垃圾收集過程的對象就越難以消亡。
    跨代引用假說:跨代引用相對于同代引用來說僅占極少數(shù)族沃。
    設(shè)計原則:收集器應(yīng)該將Java堆劃分出不同的區(qū)域频祝,然后回收對象依據(jù)其年齡(年齡即對象熬過垃圾收集過程的次數(shù))分配到不同的區(qū)域之中存儲。顯而易見脆淹,如果一個區(qū)域中大多數(shù)對象都是朝生夕滅常空,難以熬過垃圾收集過程那么把它們集中放在儀器,每次回收只關(guān)注如何保留少量存活等盖溺。Java堆分區(qū)之后窟绷,才有了Minor GC、Major GC咐柜、Full GC這樣的回收類型的劃分才會有按照存亡特征相匹配的算法兼蜈,“標記-復(fù)制、標記-清除拙友、標記-整理算法为狸。但是有很大的困難,例如跨代引用遗契。針對跨代引用辐棒,完全掃描不合適,在新生代建立一個記憶集牍蜂,Remembered Set漾根,這個結(jié)構(gòu)把老年代劃分成若干小塊,引用的小塊里的對象才會被加入到GC Roots進行掃描鲫竞,雖然賦值時會增加開銷辐怕,但是劃算的。
    2)標記清除算法(三種算法比較了解从绘,不做過多贅述)
    3)標記復(fù)制算法
    4)標記整理算法(老年代)
    備注:是否移動回收后的存活對象是一項優(yōu)缺點并存的風(fēng)險:如果移動對象寄疏,老年代這種大量對象存活是牢,移動就是一種比較復(fù)雜的過程,移動對象時必須暫停用戶線程陕截,stop the world驳棱,但是如果按照標記清除那樣子考慮,就會產(chǎn)生空間碎片問題农曲,所以移動對象與否都會有問題社搅,移動內(nèi)存回收會更復(fù)雜,不移動則內(nèi)存分配時更復(fù)雜乳规,從停頓時間來看罚渐,不移動停頓時間更短,但從吞吐量上來看驯妄,移動會劃算荷并,因為內(nèi)存分配和訪問垃圾收集頻率比要高的多,這部分耗時增加青扔,總吞吐量下降源织。如果關(guān)注吞吐量,Parallel Scavenge收集器基于整理算法微猖,關(guān)注延遲的則基于清除算法CMS谈息。
  3. HotSpot的算法細節(jié)實現(xiàn)
    1)根節(jié)點枚舉
    1.查找能作為GC Roots的引用,迄今為止所有收集器在根節(jié)點枚舉這一步都必須暫停用戶線程凛剥,根結(jié)點枚舉始終必須在一個能保障一致性的快照中才得以進行侠仇,一致性就是在某個時間點停下來,原因是不暫停程序犁珠,根節(jié)點集合的對象引用關(guān)系還在不斷變化逻炊。
    2.算法產(chǎn)生原因:當(dāng)用戶線程暫停下來之后,其實并不需要一個不漏的檢查上下文和全局引用的位置犁享,虛擬機通過OopMap的數(shù)據(jù)結(jié)構(gòu)來知道直接哪些地方存著對象引用余素。
    3.當(dāng)虛擬機加載完成時,即時編譯過程中炊昆,也會在特定的位置記錄下棧里和寄存器哪些位置是引用桨吊,所以直接掃描。
    2)安全點
    1.原因:導(dǎo)致OopMap內(nèi)容變化的指令非常多凤巨,如果為每一條指令都生成對應(yīng)的OopMap视乐,那將需要大量的額外存儲空間。
    2.在特定位置記錄了這些信息敢茁,這些位置被稱為安全點佑淀,也就是決定了用戶程序執(zhí)行時并非在代碼指令流的任意位置都能停頓下來開始垃圾收集,而強制要求到達安全點開始收集卷要。
    3.選定安全點的標準是渣聚,是否具有讓程序長時間執(zhí)行的特征。最明顯的特征是指令序列的復(fù)用僧叉,例如方法調(diào)用奕枝、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等瓶堕。所以只有這些功能的指令才會產(chǎn)生安全點隘道。
    問題:如何讓垃圾收集器發(fā)生時讓所有線程(不包括JNI——native)都跑到最近的安全點,然后停頓下來郎笆。
    方案:搶先式中斷和主動式中斷
    搶先式中斷:不需要線程的執(zhí)行代碼主動去配合谭梗,在垃圾收集發(fā)生時,系統(tǒng)首先把所有用戶線程全部中斷宛蚓,如果發(fā)現(xiàn)有用戶線程中斷的地方不在安全點上激捏,就恢復(fù)這條線程執(zhí)行,讓它跑到安全點上再中斷凄吏。
    主動式中斷:當(dāng)垃圾收集器需要中斷線程時远舅,不直接對線程操作,僅僅簡單在未來設(shè)置一個標志位痕钢,各個線程執(zhí)行過程中會不停主動去輪詢(輪詢(Polling)是一種CPU決策如何提供周邊設(shè)備服務(wù)的方式图柏。輪詢法的概念是:由CPU定時發(fā)出詢問,依序詢問每一個周邊設(shè)備是否需要其服務(wù)任连,有即給予服務(wù)蚤吹,服務(wù)結(jié)束后再問下一個周邊,接著不斷周而復(fù)始随抠。)這個標志裁着,一旦發(fā)現(xiàn)中斷標志為真就自己在最近的安全點上主動中斷掛起。(Hotspot實現(xiàn))
    3)安全區(qū)域
    1.原因:當(dāng)程序不執(zhí)行的時候拱她,不執(zhí)行就是沒有分配處理器時間跨算,典型的場景便是用戶線程處于Sleep和Blocked狀態(tài),這時用戶線程就無法響應(yīng)虛擬機的中斷請求椭懊,所以引入“安全區(qū)域”诸蚕。
    2.安全區(qū)域是指能夠確保在某一段代碼片段中,引用關(guān)系不會發(fā)生變化氧猬,因此背犯,在這個區(qū)域中任意地方開始垃圾收集都是安全的。當(dāng)用戶線程執(zhí)行到安全區(qū)域里面的代碼時盅抚,首先標識自己進入安全區(qū)域漠魏,當(dāng)這段時間里虛擬機要發(fā)起垃圾收集時就不必去管這些已經(jīng)聲明自己在安全區(qū)域內(nèi)的線程了。當(dāng)離開安全區(qū)域時妄均,它要檢查虛擬機是否已經(jīng)完成了根節(jié)點枚舉(或者其他需要暫停用戶線程的行為)柱锹,如果完成了哪自,那線程就當(dāng)沒事發(fā)生過,繼續(xù)執(zhí)行否則它就必須一直等待禁熏,直到收到可以離開安全區(qū)域的信號為止壤巷。
    4)記憶集與卡表
    1.原因:解決對象跨代引用所帶來的問題。
    2.垃圾收集器在新生代建立記憶集瞧毙,以避免把整個老年代加入GC Roots掃描范圍
    3.記憶集是一種用于記錄從非收集區(qū)域指向收集區(qū)域的指針集合的抽象數(shù)據(jù)結(jié)構(gòu)胧华。
    4.列舉一些可供選擇的記錄精度:
    字長精度:每個記錄精確到一個機器字長(就是處理器的尋址位數(shù),如32位/64位宙彪,這個精度決定了機器訪問物理內(nèi)存地址的指針長度)矩动,該字包含跨代指針。
    對象精度:每個記錄精確到一個對象释漆,該對象里有字段含有跨代指針悲没。
    卡精度:每個記錄精確到一塊內(nèi)存區(qū)域,該區(qū)域內(nèi)有對象含有跨代指針男图。
    第三種“卡精度”所指的就是用一種稱為“卡表”的方式去實現(xiàn)記憶集檀训。它定義了記憶集的記錄精度、與堆內(nèi)存的映射關(guān)系等享言【欤卡表最簡單的形式可以只是一個字節(jié)數(shù)組,字節(jié)數(shù)組的每一個元素都對應(yīng)著其標識的內(nèi)存區(qū)域中的一塊特定大小的內(nèi)存塊览露,這個內(nèi)存塊被稱作“卡頁”荧琼。卡頁的大小都是以2的N次冪的字節(jié)數(shù)差牛,例如Hotspot中卡頁是2的9次冪命锄,512字節(jié)
    一個卡頁的內(nèi)存中通常包含不止一個對象,只要卡頁內(nèi)有一個或者更多的對象字段存在跨代指針偏化,那就將對應(yīng)卡表的數(shù)組元素的值標識為1脐恩,稱為元素變臟,沒有則標識為0侦讨。在垃圾收集發(fā)生時驶冒,只要篩選出卡表中變臟的元素就能輕易得出哪些卡頁內(nèi)存塊包含跨代指針,把他們加入GC Roots中一并掃描韵卤。
    5)寫屏障/偽共享
    1.原因:卡表元素如何維護問題骗污,例如他們何時變臟、誰來把他們變臟沈条。
    2.有其他分代區(qū)域中對象引用了本區(qū)域?qū)ο髸r需忿,其對應(yīng)的卡表元素就應(yīng)該變臟,時間點應(yīng)該發(fā)生在引用類型字段賦值的那一刻。
    3.Hotspot虛擬機通過寫屏障技術(shù)維護卡表狀態(tài)屋厘,寫屏障可以看作在虛擬機層面對“引用類型字段賦值”這個動作的AOP切面涕烧,寫后屏障和寫前屏障。
    偽共享:高并發(fā)場景下產(chǎn)生汗洒,偽共享是處理并發(fā)底層細節(jié)時议纯,一種經(jīng)常要考慮的問題,現(xiàn)代中央處理器的緩存系統(tǒng)中是以緩存行(Cache Line)為單位存儲的仲翎,當(dāng)多線程修改互相獨立的變量時痹扇,如果這些變量恰好共享同一個緩存行铛漓,就會彼此影響(寫回溯香、無效化或者同步)而導(dǎo)致性能降低,這就是偽共享問題浓恶。
    解決方法:不采用無條件的寫屏障玫坛,而是先檢查卡表標記,只有當(dāng)該卡表元素未被標記過時才將其標記為變臟包晰,不過會增加額外的開銷湿镀,但能夠避免偽共享問題,兩者各有性能損耗伐憾。
    6)并發(fā)可達性分析/增量更新/原始快照
    1.垃圾收集器基本上都是依靠可達性分析算法來判定對象是否存活勉痴,可達性分析算法理論上全過程都基于一個能保障一致性的快照才能分析,這意味著必須全程凍結(jié)用戶線程的運行树肃。在根節(jié)點枚舉這個步驟中蒸矛,由于GC Roots相比起整個Java堆中全部對象畢竟還是少數(shù),且在優(yōu)化技巧(OopMap)的加持下胸嘴,非常短暫了雏掠。但是從GC Roots 再繼續(xù)往下遍歷對象圖,這一步驟的停頓時間就必然和Java堆容量直接成正比例關(guān)系了劣像。
    2.標記階段是所有追蹤式垃圾收集算法的共同特征乡话,如果這個階段會隨著堆變大而等比例增加停頓時間,必須削減這部分停頓時間耳奕。先搞清楚為什么必須在一個能保障一致性的快照上才能進行對象圖的遍歷绑青?
    白色:對象尚未被垃圾收集器訪問過,顯然在剛開始的階段屋群,所有對象都是白色的时迫,若分析結(jié)束的階段,仍然是白色的對象谓晌,即代表不可達掠拳。
    黑色:表示對象已經(jīng)被垃圾收集器訪問過,且這個對象的所有引用都已經(jīng)掃描過纸肉。黑色的對象代表已經(jīng)掃描過溺欧,它是安全存活的喊熟,如果有其他對象引用指向了黑色對象,無須重新掃描一遍姐刁。
    灰色:表示對象已經(jīng)被垃圾收集器訪問過芥牌,但這個對象上至少存在一個引用還沒被掃描過。
    如果不暫停用戶線程聂使,可能會導(dǎo)致收集器在對象圖上標記顏色壁拉,同時用戶線程在修改引用關(guān)系——即修改對象圖結(jié)構(gòu),可能出現(xiàn)兩種后果:一種是把原本消亡的對象錯誤標記為存活柏靶,只不過產(chǎn)生了一點逃過本次收集的浮動垃圾而已弃理。第二種是把原本存活的對象錯誤標記為已消亡,這是致命的后果屎蜓。
    Wilson于1994年在理論上證明了痘昌,當(dāng)且僅當(dāng)以下兩個條件同時滿足時,會產(chǎn)生對象消失問題炬转,即原本應(yīng)該是黑色的對象被誤標為白色:
    1.賦值器插入了一條或多條從黑色對象到白色對象的新引用辆苔;
    2.賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用
    只需要破壞一個條件,就可以避免對象消失扼劈,產(chǎn)生兩種解決方案:增量更新和原始快照
    增量更新:當(dāng)黑色對象插入新的白色對象的引用關(guān)系時驻啤,就將這個新插入的引用記錄下來,等并發(fā)掃描結(jié)束之后荐吵,再將這些記錄過的引用關(guān)系中的黑色對象為根骑冗,重新掃描一次。
    原始快照:當(dāng)灰色對象要刪除指向白色對象的引用關(guān)系時捍靠,就將這個要刪除的引用記錄下來沐旨,等并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的灰色對象為根榨婆,重新掃描一次磁携。
    備注:無論是對引用關(guān)系的插入還是刪除,虛擬機的記錄操作都是通過寫屏障實現(xiàn)的良风。CMS是基于增量更新來做并發(fā)標記的谊迄、G1和Shenandoah則是用原始快照來實現(xiàn)。
  4. 幾種經(jīng)典的垃圾收集器
    吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 運行垃圾收集時間)
    1)Serial
    1.一個單線程收集器烟央,啟用時必須 stop the world统诺,采取標記-復(fù)制算法實現(xiàn)。
    2.迄今為止疑俭,它仍然是Hotspot虛擬機運行再客戶端模式下的默認新生代收集器粮呢,他是所有收集器里額外內(nèi)存消耗最小的,且沒有線程交互的開銷,專心做垃圾收集啄寡。
    3.在桌面場景以及近年來流行的部分微服務(wù)應(yīng)用中豪硅,分配給虛擬機管理的內(nèi)存一般不會特別大,收集幾十兆甚至一百兆的新生代挺物,垃圾收集的停頓時間完全可以控制在十幾懒浮、幾十毫秒,最多100毫秒以內(nèi)识藤,所以Serial對于運行在客戶端模式下的虛擬機來說是一個很好的選擇砚著。
    2)ParNew
    1.實質(zhì)上是Serial的多線程并行版本,新生代采用復(fù)制算法痴昧,多線程并行稽穆,暫停用戶線程。
    2.只能和CMS搭配使用剪个,在服務(wù)端常用秧骑,無法與Parallel Scavenge搭配使用版确,因為一個面向低延遲一個面向高吞吐量扣囊,除此之外就是,Parallel沒有分代框架绒疗,而CMS又是基于這種強分代框架下侵歇。
    3)CMS
    1.CMS收集器是一種以獲取最短回收停頓時間為目標的收集器,基于標記-清除算法吓蘑,其中包含幾個階段:
    初始標記:需要stop the world 惕虑。初始標記僅僅只是標記一下GC Roots能直接關(guān)聯(lián)到的對象,速度很快磨镶;
    并發(fā)標記:就是從GC Roots的直接關(guān)聯(lián)對象開始遍歷整個對象圖的過程溃蔫,雖然耗時較長但是不需要停頓用戶線程;
    重新標記:需要stop the world 琳猫,則是為了修正并發(fā)標記期間伟叛,因用戶線程線程繼續(xù)運轉(zhuǎn)而導(dǎo)致的標記變動的記錄;
    并發(fā)清除:清理掉標記階段已經(jīng)死亡的對象脐嫂,由于不需要移動存活對象统刮,所以也是并發(fā)清除。
    2.缺點:
    ①:CMS對處理器資源非常敏感账千,因占用一部分線程而導(dǎo)致程序變慢侥蒙,降低總吞吐量。
    ②:CMS收集器無法處理浮動垃圾匀奏,有可能導(dǎo)致Concurrent Mode Failure失敗進而導(dǎo)致另一次完全的Stop the world 的Full GC產(chǎn)生鞭衩。由于由于垃圾收集階段用戶線程還在持續(xù)運行,那還需要預(yù)留足夠的內(nèi)存空間提供給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全填滿了再進行收集论衍,必須預(yù)留一部分空間供并發(fā)收集時的程序運行恒水,JDK6時,CMS收集器啟動閾值已經(jīng)默認提升至92%饲齐,但又會面臨另外一種風(fēng)險:要是CMS運行期間預(yù)留的內(nèi)存無法滿足程序分配新對象的需要钉凌,就會出現(xiàn)一次并發(fā)失敗,這時虛擬機不得不啟動后備預(yù)案:凍結(jié)用戶線程捂人,臨時啟動Serial Old 收集器來重新進行老年代的垃圾收集御雕。
    ③:產(chǎn)生大量碎片空間,空間碎片過多時滥搭,將會給大對象分配帶來麻煩酸纲,往往會出現(xiàn)老年代還有很多剩余空間,但就是無法找到足夠大的連續(xù)空間來分配當(dāng)前對象瑟匆,而不得不提前觸發(fā)一次Full GC時開啟內(nèi)存碎片的合并整理過程闽坡,且必須移動存活對象,是無法并發(fā)的愁溜。
    4)G1
    1.G1是里程碑式的收集器疾嗅,弱化分代概念,開創(chuàng)了面向局部收集和基于Region的內(nèi)存布局形式冕象,主要面向服務(wù)端應(yīng)用代承。
    2.設(shè)計者們希望能建立起“停頓時間模型”的收集器,停頓時間模型的意思是能夠支持指定在一個長度為M毫秒的時間片段內(nèi)渐扮,消耗在垃圾收集上的時間大概率不超過N毫秒這樣的目標论悴。
    3.G1面向堆內(nèi)存任何部分來組成回收集,進行回收墓律,衡量標準不再是它屬于哪一個分代膀估,而是哪塊內(nèi)存存放的垃圾數(shù)量最多,回收收益最大耻讽,這就是G1的Mixed GC模式察纯。
    4.G1把連續(xù)的Java堆劃分成多個大小相等的獨立區(qū)域(Region),每一個Region根據(jù)需要扮演新生代Eden空間齐饮、Survivor空間或者老年代空間捐寥。收集器能夠?qū)Π缪莶煌巧腞egion采用不同的策略去處理,這樣就能達到很好的收集效果祖驱。
    5.Region中還有一類特殊的Humongous區(qū)域握恳,專門用來存儲大對象,G1認為超過了一個Region大小的一半即可判定為大對象捺僻,每個Region取值范圍是1~32MB乡洼,為2的N次冪崇裁。對于超過了整個Region容量的超大對象,將會被存放在N個連續(xù)的Humongous Region中束昵,G1的大多數(shù)行為都把Humongous Region作為老年代的一部分開看待拔稳。
    6.每次垃圾收集根據(jù)用戶設(shè)定的允許的收集停頓時間,優(yōu)先處理回收價值最大的那部分Region锹雏。
    7.G1收集器面臨的問題:
    ①:多個Region的跨代引用問題巴比,每個Region都會維護自己的記憶集,這些記憶集會記錄下別的Region指向自己的指針礁遵,并且標記這些指針分別在哪個范圍之內(nèi)轻绞,本質(zhì)是G1記憶集是一個hash表,Key是別的Region的起始地址佣耐,Value是一個集合政勃,里面存儲的元素是卡表的索引號。這種雙向卡表結(jié)構(gòu)(卡表是我指向誰兼砖,這個結(jié)構(gòu)還有誰指向我)奸远,所以維護起來,就有著更高的內(nèi)存占用負擔(dān)讽挟,G1至少要消耗大約相當(dāng)于Java堆容量10%到20%的額外內(nèi)存來維持收集器工作懒叛。
    ②:并發(fā)標記階段如何保證收集線程與用戶線程互不干擾的運行。CMS采用增量更新的算法實現(xiàn)戏挡,而G1采用原始快照算法(SATB)實現(xiàn)芍瑞。此外垃圾收集對用戶線程的影響還體現(xiàn)在回收過程中新創(chuàng)建對象的內(nèi)存分配上晨仑,程序要繼續(xù)運行就肯定會持續(xù)由新對象被創(chuàng)建褐墅,G1為每一個Region設(shè)計了兩個名為TAMS的指針,把Region中的一部分空間劃分出來用于并發(fā)回收過程中的新對象分配洪己,并發(fā)回收時新分配的對象地址都必須要在這兩個指針位置以上妥凳。G1收集器默認在這個地址以上的對象是被隱式標記過的,即默認它們是存活的答捕。如果內(nèi)存回收速度趕不上內(nèi)存分配速度逝钥,G1收集器也要被迫凍結(jié)用戶線程,導(dǎo)致Stop the world拱镐。Full GC艘款。
    ③:如何建立起可靠的停頓預(yù)測模型?用戶通過參數(shù)指定的停頓時間只意味著垃圾收集發(fā)生之前的期望值沃琅,G1收集器的停頓時間預(yù)測模型是以衰減均值為理論基礎(chǔ)來實現(xiàn)的哗咆,在垃圾收集的過程中,G1收集器會記錄每個Region的回收耗時益眉、每個Region記憶集里臟卡數(shù)量等各個可測量的步驟花費成本晌柬,并分析得出平均值姥份、標準偏差、置信度等統(tǒng)計信息年碘。這里強調(diào)的“衰減平均值”是指它會比普通的平均值更容易受到新數(shù)據(jù)的影響澈歉,平均值代表整體平均狀態(tài),但衰減平均值更準確代表“最近的”平均狀態(tài)屿衅。換句話說埃难,Region的統(tǒng)計狀態(tài)越新越能決定回收的價值。然后通過這些信息預(yù)測現(xiàn)在開始回收的話涤久,由哪些Region組成回收集才可以在不超過期望停頓時間的約束下獲得最高的收益凯砍。
    8.運作步驟:
    初始標記:僅僅只是標記一下GC Root能直接關(guān)聯(lián)到的對象,并且修改TAMS指針的值拴竹,讓下一階段用戶線程并發(fā)運行時悟衩,能正確地在可用的Region中分配新對象,需要停頓線程栓拜。
    并發(fā)標記:從GC Root開始對堆中對象進行可達性分析座泳,遞歸掃描整個堆里地對象圖,當(dāng)掃描完成以后幕与,還需要重新處理SATB記錄下地在并發(fā)時有引用變動的對象挑势。
    最終標記:對用戶線程做另外一個短暫的暫停,用于處理并發(fā)階段結(jié)束后遺留下來的最后那少量的SATB記錄啦鸣。
    篩選回收:負責(zé)更新Region統(tǒng)計數(shù)據(jù)潮饱,對各個Region的回收價值和成本進行排序,根據(jù)用戶所期望的停頓時間來制定回收計劃诫给;可以自由選擇任意個Region構(gòu)成回收集香拉,然后把決定回收的那一部分Region的存活對象復(fù)制到空的Region中,再清理掉整個舊的Region的全部空間中狂,這里的操作涉及存活對象的移動凫碌,是必須暫停用戶線程,由多條收集器線程并行完成的胃榕。
    由此可看到盛险,G1除了并發(fā)標記之外,其余階段也是要暫停用戶線程的勋又。并非純粹追求低延遲苦掘,官方的設(shè)計目標是在延遲可控的情況下,盡可能獲得高的吞吐量楔壤。
    9.用戶指定期望的停頓時間是G1很強大的一個功能鹤啡。通常設(shè)定100~300ms。
    10.從G1開始最先進的垃圾收集器的設(shè)計導(dǎo)向都不約而同地變?yōu)槟軌驊?yīng)付應(yīng)用地內(nèi)存分配速率挺邀,而不追求一次把整個Java堆全部清理干凈揉忘。這樣跳座,應(yīng)用在分配,同時收集器在收集泣矛,只要收集器的速度能跟得上對象分配的速度疲眷,那一切就能運作得很完美
    11.CMS與G1收集器的對比
    G1優(yōu)點:指定最大停頓時間您朽、分region的內(nèi)存布局狂丝、按收益動態(tài)確定回收集。G1從整體上基于標記整理算法哗总,局部(兩個Region之間)看又是基于標記復(fù)制算法几颜,不會產(chǎn)生內(nèi)存空間碎片,收集完成后能提供規(guī)整的可用內(nèi)存讯屈。
    G1相比于CMS缺點:在用戶程序運行過程中蛋哭,G1無論是為了垃圾收集產(chǎn)生的內(nèi)存占用還是程序運行時的額外執(zhí)行負載都比CMS高。G1的記憶集可能會占整個堆容量的20%乃至更多的內(nèi)存空間涮母;從執(zhí)行負載的角度來看谆趾,CMS用寫后屏障更新維護卡表,而G1除了使用寫后屏障來維護卡表之外叛本,為了實現(xiàn)原始快照算法(SATB)沪蓬,還要使用寫前屏障來跟蹤并發(fā)時的指針變化情況。(相比較增量更新算法来候,原始快照算法能減少并發(fā)標記和重新標記階段的消耗跷叉,避免CMS在最終標記階段停頓時間過長的缺點,但會產(chǎn)生額外的負擔(dān)营搅。CMS寫屏障是同步操作云挟,而G1就不得不將其實現(xiàn)為類似消息隊列的結(jié)構(gòu),把寫前屏障和寫后屏障要做的事情放在消息隊列里剧防,然后再異步處理植锉。
    結(jié)論:小內(nèi)存上CMS好一點,大內(nèi)存G1上大多能發(fā)揮其優(yōu)勢峭拘,6GB到8GB之間。
  5. low_delay垃圾收集器
    1)ZGC
    1.ZGC收集器是一款基于Region內(nèi)存布局的狮暑,(暫時)不設(shè)分代的鸡挠,使用了讀屏障、染色指針和內(nèi)存多重映射等技術(shù)來實現(xiàn)的標記-整理算法搬男,以低延遲為首要目標的一款垃圾收集器拣展。
    2.ZGC的Region具有動態(tài)性——動態(tài)創(chuàng)建和銷毀,以及動態(tài)的區(qū)域容量大械薰洹:
    小型Region:容量固定2MB备埃,用于放置小于256KB的小對象姓惑。
    中型Region:容量固定為32MB,用于放置大于等于256KB但小于4MB的對象按脚。
    大型Region:容量不固定于毙,可以動態(tài)變化,但必須為2MB的整數(shù)倍键思,用于放置4MB以上的大對象梦染。每個大型的Region中只會存放一個大對象咆蒿,雖然名字是大型Region但最小容量可低至4MB。大型Region在ZGC的實現(xiàn)中是不會被重分配介蛉,因為復(fù)制一個大對象的代價極高。
    3.ZGC的核心問題——并發(fā)整理算法
    ZGC標志性設(shè)計——染色指針技術(shù):如果我們要在對象上存儲一些額外的溶褪、只供收集器币旧、或者虛擬機本身使用的數(shù)據(jù),通常會在對象頭中增加額外的存儲字段猿妈,如對象的哈希碼佳恬、分代年齡、鎖記錄等就是這樣存儲的于游。這種記錄方式在有對象訪問的場景下是很自然流暢的毁葱,不會有什么額外的負擔(dān)。但如果對象存在被移動過的可能性贰剥,即不能保證對象訪問能夠成功呢倾剿?能不能從指針或者與對象內(nèi)存無關(guān)的地方得到這些信息——追蹤式收集算法的標記階段就可能存在只跟指針打交道而不必涉及指針所引用的對象本身的場景。例如對象標記階段過程需要給對象打上三色標記蚌成,這些標記本質(zhì)上就只和對象的引用有關(guān)前痘,而與對象本身無關(guān)。
  6. 實戰(zhàn):內(nèi)存分配與回收策略

第二部分 虛擬機執(zhí)行子系統(tǒng) 代碼編譯的結(jié)果從本地機器碼轉(zhuǎn)變?yōu)樽止?jié)碼担忧,是存儲格式發(fā)展的一小步芹缔,卻是編程語言發(fā)展的一大步

第六章 類文件結(jié)構(gòu)

這里就不做整理了,第一次看書時就打開了一個編譯后的.class文件查看了瓶盛,具體位置等最欠,傳一張照片。



  1. Class類文件結(jié)構(gòu)
  2. 字節(jié)碼指令簡介
  3. 公有設(shè)計惩猫,私有實現(xiàn)

第七章 虛擬機類加載機制(鑒于字數(shù)過多芝硬,從第七章開始分小文章整理)

代碼編譯的結(jié)果從本地機器碼轉(zhuǎn)變?yōu)樽止?jié)碼,是存儲格式發(fā)展的一小步轧房,卻是編程語言發(fā)展的一大步拌阴。
Java虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進行校驗奶镶、轉(zhuǎn)換解析和初始化迟赃,最終形成可以被虛擬機直接使用的Java類型陪拘,這個過程被稱作虛擬機的類加載機制。
與那些在編譯時需要連接的語言不同纤壁,在Java語言里面左刽,類型的加載、連接和初始化過程都是在程序運行期間完成的摄乒,這種策略讓Java語言進行提前編譯會面臨額外的困難悠反,也會讓類加載時稍微增加一點額外的開銷,但卻為Java應(yīng)用提供了極高的擴展性和靈活性馍佑,Java天生可以動態(tài)擴展的語言特性就是依賴運行期間動態(tài)加載和動態(tài)鏈接實現(xiàn)的斋否。

  1. 類加載的時機
  2. 類加載的過程
  3. 類加載器
  4. Java模塊化系統(tǒng)

第八章 虛擬機字節(jié)碼執(zhí)行引擎

  1. 運行時棧幀結(jié)構(gòu)
  2. 方法調(diào)用
  3. 動態(tài)類型語言支持
  4. 基于棧的字節(jié)碼解釋執(zhí)行引擎

第九章 類加載及執(zhí)行子系統(tǒng)的案例與實戰(zhàn)

  1. TomCat
  2. OSGI
  3. 字節(jié)碼生成技術(shù)與動態(tài)代理技術(shù)
  4. Backport工具:Java的時光

前后端編譯 這里就只看了,泛型 自動拆箱裝箱技術(shù)

第十章 前端編譯與優(yōu)化

1.泛型拭荤、自動拆箱茵臭、裝箱與foreach循環(huán)

第十一章 后端編譯與優(yōu)化

  1. 即時編譯器
  2. 提前編譯器
  3. 編譯器優(yōu)化技術(shù)
  4. 深入理解Graal編譯器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市舅世,隨后出現(xiàn)的幾起案子旦委,更是在濱河造成了極大的恐慌,老刑警劉巖雏亚,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缨硝,死亡現(xiàn)場離奇詭異,居然都是意外死亡罢低,警方通過查閱死者的電腦和手機查辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來网持,“玉大人宜岛,你說我怎么就攤上這事」σǎ” “怎么了萍倡?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辟汰。 經(jīng)常有香客問我列敲,道長,這世上最難降的妖魔是什么莉擒? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任酿炸,我火速辦了婚禮,結(jié)果婚禮上涨冀,老公的妹妹穿的比我還像新娘。我一直安慰自己麦萤,他們只是感情好鹿鳖,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布扁眯。 她就那樣靜靜地躺著,像睡著了一般翅帜。 火紅的嫁衣襯著肌膚如雪姻檀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天涝滴,我揣著相機與錄音绣版,去河邊找鬼。 笑死歼疮,一個胖子當(dāng)著我的面吹牛杂抽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播韩脏,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缩麸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赡矢?” 一聲冷哼從身側(cè)響起杭朱,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吹散,沒想到半個月后弧械,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡空民,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年刃唐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袭景。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡唁桩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耸棒,到底是詐尸還是另有隱情荒澡,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布与殃,位于F島的核電站单山,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏幅疼。R本人自食惡果不足惜米奸,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爽篷。 院中可真熱鬧悴晰,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棕硫,卻和暖如春髓涯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哈扮。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工纬纪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滑肉。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓包各,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赦邻。 傳聞我的和親對象是個殘疾皇子髓棋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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