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