您好,登錄后才能下訂單哦!
本篇內容主要講解“JVM的垃圾回收算法工作原理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JVM的垃圾回收算法工作原理”吧!
怎么判斷對象是否可以被回收?
共有2種方法,引用計數法和可達性分析
1.引用計數法
所謂引用計數法就是給每一個對象設置一個引用計數器,每當有一個地方引用這個對象時,就將計數器加一,引用失效時,計數器就減一。當一個對象的引用計數器為零時,說明此對象沒有被引用,也就是“死對象”,將會被垃圾回收.
引用計數法有一個缺陷就是無法解決循環引用問題,也就是說當對象A引用對象B,對象B又引用者對象A,那么此時A,B對象的引用計數器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機都沒有采用這種算法。
public classReferenceFindTest{publicstaticvoidmain(String[] args){MyObject object1 = new MyObject();MyObject object2 = new MyObject();object1.object = object2;object2.object = object1;object1 = null;object2 = null;}}
2.可達性算法(引用鏈法)
該算法的思想是:從一個被稱為GC Roots的對象開始向下搜索,如果一個對象到GC Roots沒有任何引用鏈相連時,則說明此對象不可用。
在java中可以作為GC Roots的對象有以下幾種:
虛擬機棧中引用的對象 方法區類靜態屬性引用的對象 方法區常量池引用的對象 本地方法棧JNI引用的對象
雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時,一個對象比不一定會被回收。當一個對象不可達GC Root時,這個對象并不會立馬被回收,而是出于一個死緩的階段,若要被真正的回收需要經歷兩次標記。
如果對象在可達性分析中沒有與GC Root的引用鏈,那么此時就會被第一次標記并且進行一次篩選,篩選的條件是是否有必要執行finalize()方法。當對象沒有覆蓋finalize()方法或者已被虛擬機調用過,那么就認為是沒必要的。
如果該對象有必要執行finalize()方法,那么這個對象將會放在一個稱為F-Queue的對隊列中,虛擬機會觸發一個Finalize()線程去執行,此線程是低優先級的,并且虛擬機不會承諾一直等待它運行完,這是因為如果finalize()執行緩慢或者發生了死鎖,那么就會造成F-Queue隊列一直等待,造成了內存回收系統的崩潰。GC對處于F-Queue中的對象進行第二次被標記,這時,該對象將被移除”即將回收”集合,等待回收。
堆內存分代策略以及意義
策略
Java虛擬機將堆內存劃分為新生代、老年戰和永久代,永久代是HotSpaot 虛擬機特有的概念,它采用永久代的方式來實現方法區,其他的虛擬機實現沒有這一概念,而且HotSpot也有取消永久代的趨勢,在JDK 1.7中HotSpot已經開始了“去永久化”,把原本放在永久代的字符串常量池移出。永久代主要存放常量、類信息、靜態變量等數據(移植到方法區),與垃圾回收關系不大,新生代和老年代是垃圾回收的主要區域。
新生代(Young)
新生成的對象優先存放在新生代中,新生代對象朝生夕死,存活率很低,在新生代中,常規應用進行一次垃圾收集-般可以回收70% ~ 95%的空間,回收效率很高。
老年代(OldGenerationn)
在新生代中經歷了多次(具體看虛擬機配置的閥值)GC后仍然存活下來的對象會進入老年代中。老年代中的對象生命周期較長,存活率比較高,在老年代中進行GC的頻率相對而言較低,而且回收的速度也比較慢。
永久代(PermanentGenerationn)
永久代存儲類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,對這一區域而言,Java虛擬機規范指出可以不進行垃圾收集,一般而言不會進行垃圾回收。
Jdk1.6及之前: 有永久代, 常量池1.6在方法區。 Jdk1.7: 有永久代,但已經逐步“去永久代”,常量池1.7在堆。 Jdk1.8及之后: 無永久代,常量池1.8在元空間。而元空間是直接存在內存中,不在java虛擬機中的,因此元空間依賴于內存大小。當然你也可以自定義元空間大小。
意義
有了內存分代,新創建的對象會在新生代中分配內存,經過多次回收仍然存活下來的對象存放在老年代中,靜態屬性、類信息等存放在永久代中,新生代中的對象存活時間短,只需要在新生代區域中頻繁進行GC,老年代中對象生命周期長,內存回收的頻率相對較低,不需要頻繁進行回收,永久代中回收效果太差, 一般不進行垃圾回收,還可以根據不同年代的特點,采用不同的垃圾收集算法。分代垃圾收集大大提升了垃圾收集效率,這些都是JVM分代的好處。
垃圾回收算法
1.復制算法
復制算法將可用內存按容量劃分為相等的兩部分,然后每次只使用其中的一塊,當一塊內存用完時,就將還存活的對象復制到第二塊內存上,然后一次性清楚完第一塊內存,再將第二塊上的對象復制到第一塊。但是這種方式,內存的代價太高,每次基本上都要浪費一半的內存。
2.標記清除算法
是JVM垃圾回收算法中最古老的一個,該算法共分成兩個階段,第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,清除未被標記的對象。該算法的缺點是需要暫停整個應用,并且在回收以后未使用的空間是不連續,即內存碎片,會影響到存儲。
3.標記整理算法
此算法結合了標記-清楚算法和復制算法的優點,也分為兩個階段,第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,在回收不存活的對象占用的空間后,會將所有的存活對象往左端空閑空間移動,并更新對應的指針。標記-整理算法是在標記-清除算法的基礎上,又進行了對象的移動,因此成本更高,但是卻解決了內存碎片的問題,按順序排放,同時解決了復制算法所需內存空間過大的問題。
4.分代收集
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據對象存活的生命周期將內存劃分為若干個不同的區域。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外還有一個代就是永久代(Permanet Generation)。老年代的特點是每次垃圾收集時只有少量對象需要被回收,而新生代的特點是每次垃圾回收時都有大量的對象需要被回收,那么就可以根據不同代的特點采取最適合的收集算法。
a.年輕代回收算法(核心其實就是復制算法)
HotSpot將新生代劃分為三塊,-塊較大的Eden空間和兩塊較小的Survivor空間,默認比例為8: 1: 1。劃分的目的是因為HotSpot采用復制算法來回收新生代,設置這個比例是為了充分利用內存空間,減少浪費。新生成的對象在Eden區分配(大對象除外,大對象直接進入老年代) ,當Eden區沒有足夠的空間進行分配時,虛擬機將發起一次Minor GC。GC開始時,對象只會存在于Eden區和From Survivor區,To Survivor區是空的(作為保留區域)。
GC進行時,Eden區中所有存活的對象都會被復制到To Survivor區,而在FromSurvivor區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到閥值(默認為15 ,新生代中的對象每熬過一輪垃圾回收年齡值就加1 ,GC分代年齡存儲在對象的header中)的對象會被移到老年代中,沒有達到閥值的對象會被復制到To Survivor區。
接著清空Eden區和From Survivor區,新生代中存活的對象都在To Survivor區。接著, From Survivor區和To Survivor區會交換它們的角色,也就是新的To Survivor區就是上次GC清空的FromSurvivor區,新的From Survivor區就是.上次GC的To Survivor區,總之,不管怎樣都會保證To Survivor區在一輪GC后是空的(其實這就是分代收集算法中的年輕代回收算法,稍后我們會看到)。
GC時當To Survivor區沒有足夠的空間存放上一次新生代收集下來的存活對象時,需要依賴老年代進行分配擔保,將這些對象存放在老年代中。
b.老年代回收算法(回收主要以標記-整理為主)
1)在年輕代中經歷了N次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。
2)內存比新生代也大很多(大概比例是1:2),當老年代內存滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高。
c. 持久代(Permanent Generation)的回收算法
用于存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate 等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。在該區內很少發生垃圾回收,但是并不代表不發生GC,在這里進行的GC主要是對持久代里的常量池和對類型的卸載。
條件:
1)該類所有的實例都已經被回收,即Java堆中不存在該類的任何實例;
2)加載該類的ClassLoader已經被回收;
3)該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
虛擬機可以對滿足上述3個條件的無用類進行回收,此處僅僅是“可以”,而并不是和對象一樣,不使用了就必然回收!
到此,相信大家對“JVM的垃圾回收算法工作原理”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。