一篇文章帶你了解JVM垃圾回收
如何判斷對象是否死亡(兩種方法)。
簡單的介紹一下強引用、軟引用、弱引用、虛引用(虛引用與軟引用和弱引用的區(qū)別、使用軟引用能帶來的好處)。
如何判斷一個常量是廢棄常量
如何判斷一個類是無用的類
垃圾收集有哪些算法,各自的特點?
HotSpot 為什么要分為新生代和老年代?
常見的垃圾回收器有哪些?
介紹一下 CMS,G1 收集器。
Minor Gc 和 Full GC 有什么不同呢?
1.堆空間的基本結(jié)構(gòu):
現(xiàn)在的垃圾回收器基本上都采用分代垃圾回收算法,可分為新生代和老年代,新生代又可分為Eden區(qū)、From Survivor0、To Survivor區(qū)。
對象首先在eden區(qū)域分配,再經(jīng)歷一次垃圾回收后,如果對象還存會,就年齡加一,并進入From Survive區(qū),當年齡增加到一定程度(默認15歲)時,會進入老年代。對象進入老年代的年齡閾值可通過-Xx:MaxTenuringThreshold設(shè)置,這個值會在虛擬機運行過程中進行調(diào)整,HotSpot遍歷所有對象,按照年齡從小到大累計占用的區(qū)域,當累計區(qū)域超過一半時,取此時的年齡和設(shè)置的-Xx:MaxTenuringThreshold中較小值作為晉升老年代的年齡閾值。


針對 HotSpot VM 的實現(xiàn),它里面的 GC 其實準確分類只有兩大種:
部分收集 (Partial GC):
- 新生代收集(Minor GC / Young GC):只對新生代進行垃圾收集;
- 老年代收集(Major GC / Old GC):只對老年代進行垃圾收集。需要注意的是 Major GC 在有的語境中也用于指代整堆收集;
- 混合收集(MixedGC):對整個新生代和部分老年代進行垃圾收集。
整堆收集 (Full GC):
- 收集整個 Java 堆和方法區(qū)。
2.空間分配擔保機制
為了確保Major GC時,老年代有足夠的空間容納新生代的所有對象。
《深入理解Java虛擬機》第三章對于空間分配擔保的描述如下:
JDK 6 Update 24 之前,在發(fā)生 Minor GC 之前,虛擬機必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間,如果這個條件成立,那這一次 Minor GC可以確保是安全的。如果不成立,則虛擬機會先查看 -XX:HandlePromotionFailure參數(shù)的設(shè)置值是否允許擔保失敗(Handle Promotion Failure);如果允許,那會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將嘗試進行一次 MinorGC,盡管這次 Minor GC 是有風險的;如果小于,或者 -XX: HandlePromotionFailure設(shè)置不允許冒險,那這時就要改為進行一次 Full GC。
JDK 6 Update 24之后的規(guī)則變?yōu)橹灰夏甏倪B續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小,就會進行 MinorGC,否則將進行 Full GC。
3.如何判斷一個對象已經(jīng)無效
引用計數(shù)法
給對象中添加一個引用計數(shù)器,每當有一個地方引用它,計數(shù)器就加 1;當引用失效,計數(shù)器就減 1;任何時候計數(shù)器為 0 的對象就是不可能再被使用的。
這個方法實現(xiàn)簡單,效率高,但是目前主流的虛擬機中并沒有選擇這個算法來管理內(nèi)存,其最主要的原因是它很難解決對象之間相互循環(huán)引用的問題。
2.2 可達性分析算法
這個算法的基本思想就是通過一系列的稱為 “GC Roots” 的對象作為起點,從這些節(jié)點開始向下搜索,節(jié)點所走過的路徑稱為
引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的話,則證明此對象是不可用的。
可作為 GC Roots 的對象包括下面幾種:
- 虛擬機棧(棧幀中的本地變量表)中引用的對象
- 本地方法棧(Native 方法)中引用的對象
- 方法區(qū)中類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- 所有被同步鎖持有的對象
4 不可達的對象并非“非死不可”
即使在可達性分析法中不可達的對象,也并非是“非死不可”的,這時候它們暫時處于“緩刑階段”,要真正宣告一個對象死亡,至少要經(jīng)歷兩次標記過程;可達性分析法中不可達的對象被第一次標記并且進行一次篩選,篩選的條件是此對象是否有必要執(zhí)行 finalize 方法。當對象沒有覆蓋 finalize 方法,或 finalize 方法已經(jīng)被虛擬機調(diào)用過時,虛擬機將這兩種情況視為沒有必要執(zhí)行。
被判定為需要執(zhí)行的對象將會被放在一個隊列中進行第二次標記,除非這個對象與引用鏈上的任何一個對象建立關(guān)聯(lián),否則就會被真的回收。
5 如何判斷一個常量是廢棄常量?
運行時常量池主要回收的是廢棄的常量。那么,我們?nèi)绾闻袛嘁粋€常量是廢棄常量呢?
JDK1.7 之前運行時常量池邏輯包含字符串常量池存放在方法區(qū), 此時 hotspot 虛擬機對方法區(qū)的實現(xiàn)為永久代
JDK1.7字符串常量池被從方法區(qū)拿到了堆中, 這里沒有提到運行時常量池,也就是說字符串常量池被單獨拿到堆,運行時常量池剩下的東西還在方法區(qū), 也就是hotspot 中的永久代 。
JDK1.8 hotspot 移除了永久代用元空間(Metaspace)取而代之,這時候字符串常量池還在堆, 運行時常量池還在方法區(qū), 只不過方法區(qū)的實現(xiàn)從永久代變成了元空間(Metaspace)
假如在字符串常量池中存在字符串 “abc”,如果當前沒有任何 String 對象引用該字符串常量的話,就說明常量 “abc” 就是廢棄常量,如果這時發(fā)生內(nèi)存回收的話而且有必要的話,“abc” 就會被系統(tǒng)清理出常量池了。
6 如何判斷一個類是無用的類
方法區(qū)主要回收的是無用的類,那么如何判斷一個類是無用的類的呢?
判定一個常量是否是“廢棄常量”比較簡單,而要判定一個類是否是“無用的類”的條件則相對苛刻許多。類需要同時滿足下面 3 個條件才能算是 “無用的類” :
- 該類所有的實例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實例。
- 加載該類的ClassLoader 已經(jīng)被回收。 該類對應(yīng)的
- java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
7.垃圾回收算法

7.1 標記-清除算法
該算法分為“標記”和“清除”階段:首先標記出所有不需要回收的對象,在標記完成后統(tǒng)一回收掉所有沒有被標記的對象。
它是最基礎(chǔ)的收集算法,后續(xù)的算法都是對其不足進行改進得到。這種垃圾收集算法會帶來兩個明顯的問題:
效率問題
空間問題(標記清除后會產(chǎn)生大量不連續(xù)的碎片)
7.2 標記-復(fù)制算法
為了解決效率問題,“標記-復(fù)制”收集算法出現(xiàn)了。它可以將內(nèi)存分為大小相同的兩塊,每次使用其中的一塊。當這一塊的內(nèi)存使用完后,就將還存活的對象復(fù)制到另一塊去,然后再把使用的空間一次清理掉。這樣就使每次的內(nèi)存回收都是對內(nèi)存區(qū)間的一半進行回收。
7.3 標記-整理算法
根據(jù)老年代的特點提出的一種標記算法,標記過程仍然與“標記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象回收,而是讓所有存活的對象向一端移動,然后直接清理掉端邊界以外的內(nèi)存。
7.4 分代收集算法
當前虛擬機的垃圾收集都采用分代收集算法,這種算法沒有什么新的思想,只是根據(jù)對象存活周期的不同將內(nèi)存分為幾塊。一般將 java 堆分為新生代和老年代,這樣我們就可以根據(jù)各個年代的特點選擇合適的垃圾收集算法。
比如在新生代中,每次收集都會有大量對象死去,所以可以選擇”標記-復(fù)制“算法,只需要付出少量對象的復(fù)制成本就可以完成每次垃圾收集。而老年代的對象存活幾率是比較高的,而且沒有額外的空間對它進行分配擔保,所以我們必須選擇“標記-清除”或“標記-整理”算法進行垃圾收集。
總結(jié)
這篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
使用lombok注解導致mybatis-plus TypeHandler失效的解決
這篇文章主要介紹了使用lombok注解導致mybatis-plus TypeHandler失效的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
springboot設(shè)置了server.port但是沒有用,還是8080問題
這篇文章主要介紹了springboot設(shè)置了server.port但是沒有用,還是8080問題的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringCloud Feign 服務(wù)調(diào)用的實現(xiàn)
Feign是一個聲明性web服務(wù)客戶端。本文記錄多個服務(wù)之間使用Feign調(diào)用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
使用Maven創(chuàng)建和管理多模塊項目的詳細步驟
使用Maven進行多模塊項目管理是一種常見的做法,它可以幫助你組織大型項目,使其結(jié)構(gòu)更加清晰,便于維護和構(gòu)建,以下是使用Maven創(chuàng)建和管理多模塊項目的詳細步驟,需要的朋友可以參考下2024-10-10
Spring?Data?JPA?在?@Query?中使用投影的方法示例詳解
這篇文章主要介紹了Spring?Data?JPA?在?@Query?中使用投影的方法,大家需要注意如果要在 @Query 中使用投影,必須要主動聲明要查詢的字段,并且主動寫明字段的別名才行,本文通過sql代碼給大家介紹的非常詳細,需要的朋友參考下吧2022-07-07

