一篇文章帶你了解JVM垃圾回收
如何判斷對(duì)象是否死亡(兩種方法)。
簡(jiǎn)單的介紹一下強(qiáng)引用、軟引用、弱引用、虛引用(虛引用與軟引用和弱引用的區(qū)別、使用軟引用能帶來的好處)。
如何判斷一個(gè)常量是廢棄常量
如何判斷一個(gè)類是無用的類
垃圾收集有哪些算法,各自的特點(diǎn)?
HotSpot 為什么要分為新生代和老年代?
常見的垃圾回收器有哪些?
介紹一下 CMS,G1 收集器。
Minor Gc 和 Full GC 有什么不同呢?
1.堆空間的基本結(jié)構(gòu):
現(xiàn)在的垃圾回收器基本上都采用分代垃圾回收算法,可分為新生代和老年代,新生代又可分為Eden區(qū)、From Survivor0、To Survivor區(qū)。
對(duì)象首先在eden區(qū)域分配,再經(jīng)歷一次垃圾回收后,如果對(duì)象還存會(huì),就年齡加一,并進(jìn)入From Survive區(qū),當(dāng)年齡增加到一定程度(默認(rèn)15歲)時(shí),會(huì)進(jìn)入老年代。對(duì)象進(jìn)入老年代的年齡閾值可通過-Xx:MaxTenuringThreshold設(shè)置,這個(gè)值會(huì)在虛擬機(jī)運(yùn)行過程中進(jìn)行調(diào)整,HotSpot遍歷所有對(duì)象,按照年齡從小到大累計(jì)占用的區(qū)域,當(dāng)累計(jì)區(qū)域超過一半時(shí),取此時(shí)的年齡和設(shè)置的-Xx:MaxTenuringThreshold中較小值作為晉升老年代的年齡閾值。
針對(duì) HotSpot VM 的實(shí)現(xiàn),它里面的 GC 其實(shí)準(zhǔn)確分類只有兩大種:
部分收集 (Partial GC):
- 新生代收集(Minor GC / Young GC):只對(duì)新生代進(jìn)行垃圾收集;
- 老年代收集(Major GC / Old GC):只對(duì)老年代進(jìn)行垃圾收集。需要注意的是 Major GC 在有的語境中也用于指代整堆收集;
- 混合收集(MixedGC):對(duì)整個(gè)新生代和部分老年代進(jìn)行垃圾收集。
整堆收集 (Full GC):
- 收集整個(gè) Java 堆和方法區(qū)。
2.空間分配擔(dān)保機(jī)制
為了確保Major GC時(shí),老年代有足夠的空間容納新生代的所有對(duì)象。
《深入理解Java虛擬機(jī)》第三章對(duì)于空間分配擔(dān)保的描述如下:
JDK 6 Update 24 之前,在發(fā)生 Minor GC 之前,虛擬機(jī)必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果這個(gè)條件成立,那這一次 Minor GC可以確保是安全的。如果不成立,則虛擬機(jī)會(huì)先查看 -XX:HandlePromotionFailure參數(shù)的設(shè)置值是否允許擔(dān)保失敗(Handle Promotion Failure);如果允許,那會(huì)繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小,如果大于,將嘗試進(jìn)行一次 MinorGC,盡管這次 Minor GC 是有風(fēng)險(xiǎn)的;如果小于,或者 -XX: HandlePromotionFailure設(shè)置不允許冒險(xiǎn),那這時(shí)就要改為進(jìn)行一次 Full GC。
JDK 6 Update 24之后的規(guī)則變?yōu)橹灰夏甏倪B續(xù)空間大于新生代對(duì)象總大小或者歷次晉升的平均大小,就會(huì)進(jìn)行 MinorGC,否則將進(jìn)行 Full GC。
3.如何判斷一個(gè)對(duì)象已經(jīng)無效
引用計(jì)數(shù)法
給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器就加 1;當(dāng)引用失效,計(jì)數(shù)器就減 1;任何時(shí)候計(jì)數(shù)器為 0 的對(duì)象就是不可能再被使用的。
這個(gè)方法實(shí)現(xiàn)簡(jiǎn)單,效率高,但是目前主流的虛擬機(jī)中并沒有選擇這個(gè)算法來管理內(nèi)存,其最主要的原因是它很難解決對(duì)象之間相互循環(huán)引用的問題。
2.2 可達(dá)性分析算法
這個(gè)算法的基本思想就是通過一系列的稱為 “GC Roots” 的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,節(jié)點(diǎn)所走過的路徑稱為
引用鏈,當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連的話,則證明此對(duì)象是不可用的。
可作為 GC Roots 的對(duì)象包括下面幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
- 本地方法棧(Native 方法)中引用的對(duì)象
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
- 所有被同步鎖持有的對(duì)象
4 不可達(dá)的對(duì)象并非“非死不可”
即使在可達(dá)性分析法中不可達(dá)的對(duì)象,也并非是“非死不可”的,這時(shí)候它們暫時(shí)處于“緩刑階段”,要真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷兩次標(biāo)記過程;可達(dá)性分析法中不可達(dá)的對(duì)象被第一次標(biāo)記并且進(jìn)行一次篩選,篩選的條件是此對(duì)象是否有必要執(zhí)行 finalize 方法。當(dāng)對(duì)象沒有覆蓋 finalize 方法,或 finalize 方法已經(jīng)被虛擬機(jī)調(diào)用過時(shí),虛擬機(jī)將這兩種情況視為沒有必要執(zhí)行。
被判定為需要執(zhí)行的對(duì)象將會(huì)被放在一個(gè)隊(duì)列中進(jìn)行第二次標(biāo)記,除非這個(gè)對(duì)象與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián),否則就會(huì)被真的回收。
5 如何判斷一個(gè)常量是廢棄常量?
運(yùn)行時(shí)常量池主要回收的是廢棄的常量。那么,我們?nèi)绾闻袛嘁粋€(gè)常量是廢棄常量呢?
JDK1.7 之前運(yùn)行時(shí)常量池邏輯包含字符串常量池存放在方法區(qū), 此時(shí) hotspot 虛擬機(jī)對(duì)方法區(qū)的實(shí)現(xiàn)為永久代
JDK1.7字符串常量池被從方法區(qū)拿到了堆中, 這里沒有提到運(yùn)行時(shí)常量池,也就是說字符串常量池被單獨(dú)拿到堆,運(yùn)行時(shí)常量池剩下的東西還在方法區(qū), 也就是hotspot 中的永久代 。
JDK1.8 hotspot 移除了永久代用元空間(Metaspace)取而代之,這時(shí)候字符串常量池還在堆, 運(yùn)行時(shí)常量池還在方法區(qū), 只不過方法區(qū)的實(shí)現(xiàn)從永久代變成了元空間(Metaspace)
假如在字符串常量池中存在字符串 “abc”,如果當(dāng)前沒有任何 String 對(duì)象引用該字符串常量的話,就說明常量 “abc” 就是廢棄常量,如果這時(shí)發(fā)生內(nèi)存回收的話而且有必要的話,“abc” 就會(huì)被系統(tǒng)清理出常量池了。
6 如何判斷一個(gè)類是無用的類
方法區(qū)主要回收的是無用的類,那么如何判斷一個(gè)類是無用的類的呢?
判定一個(gè)常量是否是“廢棄常量”比較簡(jiǎn)單,而要判定一個(gè)類是否是“無用的類”的條件則相對(duì)苛刻許多。類需要同時(shí)滿足下面 3 個(gè)條件才能算是 “無用的類” :
- 該類所有的實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例。
- 加載該類的ClassLoader 已經(jīng)被回收。 該類對(duì)應(yīng)的
- java.lang.Class 對(duì)象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
7.垃圾回收算法
7.1 標(biāo)記-清除算法
該算法分為“標(biāo)記”和“清除”階段:首先標(biāo)記出所有不需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有沒有被標(biāo)記的對(duì)象。
它是最基礎(chǔ)的收集算法,后續(xù)的算法都是對(duì)其不足進(jìn)行改進(jìn)得到。這種垃圾收集算法會(huì)帶來兩個(gè)明顯的問題:
效率問題
空間問題(標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的碎片)
7.2 標(biāo)記-復(fù)制算法
為了解決效率問題,“標(biāo)記-復(fù)制”收集算法出現(xiàn)了。它可以將內(nèi)存分為大小相同的兩塊,每次使用其中的一塊。當(dāng)這一塊的內(nèi)存使用完后,就將還存活的對(duì)象復(fù)制到另一塊去,然后再把使用的空間一次清理掉。這樣就使每次的內(nèi)存回收都是對(duì)內(nèi)存區(qū)間的一半進(jìn)行回收。
7.3 標(biāo)記-整理算法
根據(jù)老年代的特點(diǎn)提出的一種標(biāo)記算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象回收,而是讓所有存活的對(duì)象向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。
7.4 分代收集算法
當(dāng)前虛擬機(jī)的垃圾收集都采用分代收集算法,這種算法沒有什么新的思想,只是根據(jù)對(duì)象存活周期的不同將內(nèi)存分為幾塊。一般將 java 堆分為新生代和老年代,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法。
比如在新生代中,每次收集都會(huì)有大量對(duì)象死去,所以可以選擇”標(biāo)記-復(fù)制“算法,只需要付出少量對(duì)象的復(fù)制成本就可以完成每次垃圾收集。而老年代的對(duì)象存活幾率是比較高的,而且沒有額外的空間對(duì)它進(jìn)行分配擔(dān)保,所以我們必須選擇“標(biāo)記-清除”或“標(biāo)記-整理”算法進(jìn)行垃圾收集。
總結(jié)
這篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Spring中@ConditionalOnProperty注解的作用詳解
這篇文章主要介紹了Spring中@ConditionalOnProperty注解的作用詳解,@ConditionalOnProperty注解主要是用來判斷配置文件中的內(nèi)容來決定配置類是否生效用的,如果條件不匹配,則配置類不生效,需要的朋友可以參考下2024-01-01Java實(shí)現(xiàn)Token登錄驗(yàn)證的項(xiàng)目實(shí)踐
本文主要介紹了Java實(shí)現(xiàn)Token登錄驗(yàn)證的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Java 創(chuàng)建線程的兩個(gè)方法詳解及實(shí)例
這篇文章主要介紹了Java 創(chuàng)建線程的兩個(gè)方法詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03java 地心坐標(biāo)系(ECEF)和WGS-84坐標(biāo)系(WGS84)互轉(zhuǎn)的實(shí)現(xiàn)
這篇文章主要介紹了java 地心坐標(biāo)系(ECEF)和WGS-84坐標(biāo)系(WGS84)互轉(zhuǎn)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09SpringBoot對(duì)SSL的支持實(shí)現(xiàn)
本文主要介紹了SpringBoot對(duì)SSL的支持實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08