新手入門Jvm--Jvm垃圾回收
1. Jvm垃圾回收
Java虛擬機(jī)主要分為五大模塊:類裝載器子系統(tǒng)、運(yùn)行時(shí)數(shù)據(jù)區(qū)、執(zhí)行引擎、本地方法接口和垃圾收集模塊。其中垃圾收集模塊在Java虛擬機(jī)規(guī)范中并沒(méi)有要求Java虛擬機(jī)垃圾收集,但是在沒(méi)有發(fā)明無(wú)限的內(nèi)存之前,大多數(shù)JVM實(shí)現(xiàn)都是有垃圾收集的。
Java堆是內(nèi)存管理中最大的一塊,所有的線程共享這一塊內(nèi)容,同時(shí)該部分也是垃圾收集器的主要區(qū)域。
虛擬機(jī)的垃圾回收機(jī)制是完善的,動(dòng)態(tài)內(nèi)存分配和回收是比較成熟的,在內(nèi)存管理機(jī)制中,大部分都不需要我們考慮內(nèi)存回收,只有Java堆和方法區(qū)需要我們考慮處理內(nèi)存問(wèn)題。一般的對(duì)于內(nèi)存回收首先就是判斷某一個(gè)部分是生存還是死亡,主要是通過(guò)下面二種算法:
其一是引用計(jì)數(shù)算法,本算法實(shí)現(xiàn)簡(jiǎn)單,判定的效率也是比較高的,很多的軟件都使用了該算法,但是主流的Java并沒(méi)有選擇該算法,核心的問(wèn)題是該算法難以處理對(duì)象之間相互調(diào)用的問(wèn)題。
其二是稱可達(dá)性分析算法,該算法核心思想是依靠判斷對(duì)象是否存活來(lái)實(shí)現(xiàn)的,本算法是通過(guò)一系列的GC ROOTS的對(duì)象作為起始點(diǎn),采用搜索的算法遍歷引用鏈,如果搜索過(guò)程中沒(méi)有發(fā)現(xiàn)該節(jié)點(diǎn),則認(rèn)為該節(jié)點(diǎn)是不可達(dá)的,即可回收的,在Java里面,一般可以使用該算法處理問(wèn)題。
2. 作用域
- JVM 堆
- 年輕代
- 老年代
- 元空間
3. 分類
當(dāng)前虛擬機(jī)的垃圾收集都采用分代收集算法,這種算法沒(méi)有什么新的思想,只是根據(jù)對(duì)象存活周期的不同將內(nèi)存分為幾塊。一般將java堆分為新生代和老年代,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法。
4. 垃圾回收算法
4.1 標(biāo)記-復(fù)制算法
為了解決效率問(wèn)題,“復(fù)制”收集算法出現(xiàn)了。它可以將內(nèi)存分為大小相同的兩塊,每次使用其中的一塊。當(dāng)這一塊的內(nèi)存使用完后,就將還存活的對(duì)象復(fù)制到另一塊去,然后再把使用的空間一次清理掉。這樣就使每次的內(nèi)存回收都是對(duì)內(nèi)存區(qū)間的一半進(jìn)行回收
4.2 標(biāo)記-清除算法
算法分為“標(biāo)記”和“清除”階段:標(biāo)記存活的對(duì)象, 統(tǒng)一回收所有未被標(biāo)記的對(duì)象(一般選擇這種);也可以反過(guò)來(lái),標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象 。它是最基礎(chǔ)的收集算法,比較簡(jiǎn)單,但是會(huì)帶來(lái)兩個(gè)明顯的問(wèn)題:
- 效率問(wèn)題 (如果需要標(biāo)記的對(duì)象太多,效率不高)
- 空間問(wèn)題(標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的碎片)
4.3 標(biāo)記-整理算法
根據(jù)老年代的特點(diǎn)特出的一種標(biāo)記算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象回收,而是讓所有存活的對(duì)象向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
5. 垃圾收集器
5.1 Serial收集器(-XX:+UseSerialGC -XX:+UseSerialOldGC)
Serial(串行)收集器是最基本、歷史最悠久的垃圾收集器了。大家看名字就知道這個(gè)收集器是一個(gè)單線程收集器了。它的 “單線程” 的意義不僅僅意味著它只會(huì)使用一條垃圾收集線程去完成垃圾收集工作,更重要的是它在進(jìn)行垃圾收集工作的時(shí)候必須暫停其他所有的工作線程( “Stop The World” ),直到它收集結(jié)束。
新生代采用復(fù)制算法,老年代采用標(biāo)記-整理算法。
5.2 Parallel Scavenge收集器(-XX:+UseParallelGC(年輕代),-XX:+UseParallelOldGC(老年代))
Parallel收集器其實(shí)就是Serial收集器的多線程版本,除了使用多線程進(jìn)行垃圾收集外,其余行為(控制參數(shù)、收集算法、回收策略等等)和Serial收集器類似。默認(rèn)的收集線程數(shù)跟cpu核數(shù)相同,當(dāng)然也可以用參數(shù)(-XX:ParallelGCThreads)指定收集線程數(shù),但是一般不推薦修改。
Parallel Scavenge收集器關(guān)注點(diǎn)是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的關(guān)注點(diǎn)更多的是用戶線程的停頓時(shí)間(提高用戶體驗(yàn))。所謂吞吐量就是CPU中用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值。 Parallel Scavenge收集器提供了很多參數(shù)供用戶找到最合適的停頓時(shí)間或最大吞吐量,如果對(duì)于收集器運(yùn)作不太了解的話,可以選擇把內(nèi)存管理優(yōu)化交給虛擬機(jī)去完成也是一個(gè)不錯(cuò)的選擇。新生代采用復(fù)制算法,老年代采用標(biāo)記-整理算法。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本。使用多線程和“標(biāo)記-整理”算法。在注重吞吐量以及CPU資源的場(chǎng)合,都可以優(yōu)先考慮 Parallel Scavenge收集器和Parallel Old收集器(JDK8默認(rèn)的新生代和老年代收集器)。
5.3 ParNew收集器(-XX:+UseParNewGC)
ParNew收集器其實(shí)跟Parallel收集器很類似,區(qū)別主要在于它可以和CMS收集器配合使用。
新生代采用復(fù)制算法,老年代采用標(biāo)記-整理算法。
它是許多運(yùn)行在Server模式下的虛擬機(jī)的首要選擇,除了Serial收集器外,只有它能與CMS收集器(真正意義上的并發(fā)收集器,后面會(huì)介紹到)配合工作。
5.4 CMS收集器(-XX:+UseConcMarkSweepGC(old))
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。它非常符合在注重用戶體驗(yàn)的應(yīng)用上使用,它是HotSpot虛擬機(jī)第一款真正意義上的并發(fā)收集器,它第一次實(shí)現(xiàn)了讓垃圾收集線程與用戶線程(基本上)同時(shí)工作。
從名字中的Mark Sweep這兩個(gè)詞可以看出,CMS收集器是一種 “標(biāo)記-清除”算法實(shí)現(xiàn)的,它的運(yùn)作過(guò)程相比于前面幾種垃圾收集器來(lái)說(shuō)更加復(fù)雜一些。整個(gè)過(guò)程分為四個(gè)步驟:
- 初始標(biāo)記: 暫停所有的其他線程(STW),并記錄下gc roots直接能引用的對(duì)象,速度很快。
- 并發(fā)標(biāo)記: 并發(fā)標(biāo)記階段就是從GC Roots的直接關(guān)聯(lián)對(duì)象開(kāi)始遍歷整個(gè)對(duì)象圖的過(guò)程, 這個(gè)過(guò)程耗時(shí)較長(zhǎng)但是不需要停頓用戶線程, 可以與垃圾收集線程一起并發(fā)運(yùn)行。因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行,可能會(huì)有導(dǎo)致已經(jīng)標(biāo)記過(guò)的對(duì)象狀態(tài)發(fā)生改變。
- 重新標(biāo)記: 重新標(biāo)記階段就是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段的時(shí)間稍長(zhǎng),遠(yuǎn)遠(yuǎn)比并發(fā)標(biāo)記階段時(shí)間短。主要用到三色標(biāo)記里的增量更新算法(見(jiàn)下面詳解)做重新標(biāo)記。
- 并發(fā)清理: 開(kāi)啟用戶線程,同時(shí)GC線程開(kāi)始對(duì)未標(biāo)記的區(qū)域做清掃。這個(gè)階段如果有新增對(duì)象會(huì)被標(biāo)記為黑色不做任何處理(見(jiàn)下面三色標(biāo)記算法詳解)。
- 并發(fā)重置:重置本次GC過(guò)程中的標(biāo)記數(shù)據(jù)。
從它的名字就可以看出它是一款優(yōu)秀的垃圾收集器,主要優(yōu)點(diǎn):并發(fā)收集、低停頓。但是它有下面幾個(gè)
明顯的缺點(diǎn):
- 對(duì)CPU資源敏感(會(huì)和服務(wù)搶資源);
- 無(wú)法處理浮動(dòng)垃圾(在并發(fā)標(biāo)記和并發(fā)清理階段又產(chǎn)生垃圾,這種浮動(dòng)垃圾只能等到下一次gc再清理了);
它使用的回收算法-“標(biāo)記-清除”算法會(huì)導(dǎo)致收集結(jié)束時(shí)會(huì)有大量空間碎片產(chǎn)生,當(dāng)然通過(guò)參數(shù)-XX:+UseCMSCompactAtFullCollection可以讓jvm在執(zhí)行完標(biāo)記清除后再做整理執(zhí)行過(guò)程中的不確定性,會(huì)存在上一次垃圾回收還沒(méi)執(zhí)行完,然后垃圾回收又被觸發(fā)的情況,特別是在并發(fā)標(biāo)記和并發(fā)清理階段會(huì)出現(xiàn),一邊回收,系統(tǒng)一邊運(yùn)行,也許沒(méi)回收完就再次觸發(fā)full gc,也就是"concurrent mode failure",此時(shí)會(huì)進(jìn)入stop the world,用serial old垃圾收集器來(lái)回收
5.5 CMS的相關(guān)核心參數(shù)
-XX:+UseConcMarkSweepGC:?jiǎn)⒂胏ms
-XX:ConcGCThreads:并發(fā)的GC線程數(shù)
-XX:+UseCMSCompactAtFullCollection:FullGC之后做壓縮整理(減少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后壓縮一次,默認(rèn)是0,代表每次FullGC后都會(huì)壓縮一次
-XX:CMSInitiatingOccupancyFraction: 當(dāng)老年代使用達(dá)到該比例時(shí)會(huì)觸發(fā)FullGC(默認(rèn)是92,這是百分比)
-XX:+UseCMSInitiatingOccupancyOnly:只使用設(shè)定的回收閾值(-XX:CMSInitiatingOccupancyFraction設(shè)定的值),如果不指定,JVM僅在第一次使用設(shè)定值,后續(xù)則會(huì)自動(dòng)調(diào)整
-XX:+CMSScavengeBeforeRemark:在CMS GC前啟動(dòng)一次minor gc,降低CMS GC標(biāo)記階段(也會(huì)對(duì)年輕代一起做標(biāo)記,如果在minor gc就干掉了很多對(duì)垃圾對(duì)象,標(biāo)記階段就會(huì)減少一些標(biāo)記時(shí)間)時(shí)的開(kāi)銷,一般CMS的GC耗時(shí) 80%都在標(biāo)記階段
-XX:+CMSParallellnitialMarkEnabled:表示在初始標(biāo)記的時(shí)候多線程執(zhí)行,縮短STW
-XX:+CMSParallelRemarkEnabled:在重新標(biāo)記的時(shí)候多線程執(zhí)行,縮短STW;
6. 垃圾收集底層算法實(shí)現(xiàn)
- 三色標(biāo)記
在并發(fā)標(biāo)記的過(guò)程中,因?yàn)闃?biāo)記期間應(yīng)用線程還在繼續(xù)跑,對(duì)象間的引用可能發(fā)生變化,多標(biāo)和漏標(biāo)的情況就有可能發(fā)生。
這里我們引入“三色標(biāo)記”來(lái)給大家解釋下,把Gcroots可達(dá)性分析遍歷對(duì)象過(guò)程中遇到的對(duì)象, 按照“是否訪問(wèn)過(guò)”這個(gè)條件標(biāo)記成以下三種顏色:
- 黑色: 表示對(duì)象已經(jīng)被垃圾收集器訪問(wèn)過(guò), 且這個(gè)對(duì)象的所有引用都已經(jīng)掃描過(guò)。 黑色的對(duì)象代表已經(jīng)掃描過(guò), 它是安全存活的, 如果有其他對(duì)象引用指向了黑色對(duì)象, 無(wú)須重新掃描一遍。 黑色對(duì)象不可能直接(不經(jīng)過(guò)灰色對(duì)象) 指向某個(gè)白色對(duì)象。
- 灰色: 表示對(duì)象已經(jīng)被垃圾收集器訪問(wèn)過(guò), 但這個(gè)對(duì)象上至少存在一個(gè)引用還沒(méi)有被掃描過(guò)。
- 白色: 表示對(duì)象尚未被垃圾收集器訪問(wèn)過(guò)。 顯然在可達(dá)性分析剛剛開(kāi)始的階段, 所有的對(duì)象都是白色的, 若在分析結(jié)束的階段, 仍然是白色的對(duì)象, 即代表不可達(dá)。
7 .總結(jié)
Jvm優(yōu)化主要是防止fullgc,縮短STW時(shí)間,杜絕OOM出現(xiàn),而實(shí)現(xiàn)這一手段主要是依賴?yán)厥諜C(jī)制,具體來(lái)說(shuō)就是垃圾回收器,而垃圾回收器又分了好多種,單線程,并發(fā),回收算法的差異性等等,所以要做到深度優(yōu)化,必須理解其底層機(jī)制。
相關(guān)文章
MyBatis-Plus中實(shí)現(xiàn)自定義復(fù)雜排序邏輯的詳細(xì)步驟
這篇文章主要介紹了MyBatis-Plus中實(shí)現(xiàn)自定義復(fù)雜排序邏輯,通過(guò)使用MyBatis-Plus的QueryWrapper和SQL原始片段,我們可以靈活地實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)排序邏輯,這種方法尤其適用于需要對(duì)數(shù)據(jù)進(jìn)行特定規(guī)則排序的場(chǎng)景,需要的朋友可以參考下2024-07-07springboot如何通過(guò)SSH連接遠(yuǎn)程服務(wù)器
這篇文章主要介紹了springboot如何通過(guò)SSH連接遠(yuǎn)程服務(wù)器問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07使用spring stream發(fā)送消息代碼實(shí)例
這篇文章主要介紹了使用spring stream發(fā)送消息代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05SpringBoot JavaMailSender發(fā)送郵件功能
這篇文章主要為大家詳細(xì)介紹了SpringBoot JavaMailSender發(fā)送郵件功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04SpringBoot?@InitBinder注解綁定請(qǐng)求參數(shù)的過(guò)程詳解
這篇文章主要介紹了SpringBoot?@InitBinder注解綁定請(qǐng)求參數(shù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04Java程序執(zhí)行過(guò)程及內(nèi)存機(jī)制詳解
本講將介紹Java代碼是如何一步步運(yùn)行起來(lái)的,還會(huì)介紹Java程序所占用的內(nèi)存是被如何管理的:堆、棧和方法區(qū)都各自負(fù)責(zé)存儲(chǔ)哪些內(nèi)容,感興趣的朋友跟隨小編一起看看吧2020-12-12在SpringBoot: SpringBoot里面創(chuàng)建導(dǎo)出Excel的接口教程
這篇文章主要介紹了在SpringBoot: SpringBoot里面創(chuàng)建導(dǎo)出Excel的接口教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10如何將JSP/Servlet項(xiàng)目轉(zhuǎn)換為Spring Boot項(xiàng)目
這篇文章主要介紹了如何將JSP/Servlet項(xiàng)目轉(zhuǎn)換為Spring Boot項(xiàng)目,幫助大家更好的利用springboot進(jìn)行網(wǎng)絡(luò)編程,感興趣的朋友可以了解下2020-10-10