Java垃圾回收機(jī)制(GC)

一、垃圾回收機(jī)制的基本概念

1.什么是垃圾回收扣讼?

Garbage Collection 垃圾收集鲁冯。這里所謂的垃圾指的是在系統(tǒng)運(yùn)行過程當(dāng)中所產(chǎn)生的一些無用的對象,這些對象占據(jù)著一定的內(nèi)存空間中姜,如果長期不被釋放消玄,可能導(dǎo)致OOM(堆溢出)。

2.JVM GC回收哪個(gè)區(qū)域內(nèi)的垃圾丢胚?

內(nèi)存區(qū)域中的程序計(jì)數(shù)器翩瓜、虛擬機(jī)棧、本地方法棧這3個(gè)區(qū)域是線程獨(dú)占區(qū)域携龟,隨著線程而生兔跌,線程而滅;方法結(jié)束或者線程結(jié)束時(shí)骨宠,內(nèi)存會(huì)被自動(dòng)釋放浮定,因此不需要考慮回收的問題。

Java堆和方法區(qū)則不同层亿,是線程共享區(qū)域桦卒,一個(gè)接口中的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不同,一個(gè)方法中的多個(gè)分支需要的內(nèi)存也可能不一樣匿又,我們只有在程序處于運(yùn)行期間時(shí)才能知道會(huì)創(chuàng)建哪些對象方灾,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的,GC關(guān)注的也是這部分內(nèi)存碌更,如果涉及到“內(nèi)存”分配與回收也僅指著一部分內(nèi)存裕偿。

3.Java中的對象引用

(1)強(qiáng)引用(Strong Reference):如“Object obj = new Object()”,這類引用是Java程序中最普遍的痛单。只要強(qiáng)引用還存在嘿棘,垃圾收集器就永遠(yuǎn)不會(huì)回收掉被引用的對象。

(2)軟引用(Soft Reference):它用來描述一些可能還有用旭绒,但并非必須的對象鸟妙。在系統(tǒng)內(nèi)存不夠用時(shí)焦人,這類引用關(guān)聯(lián)的對象將被垃圾收集器回收。JDK1.2之后提供了SoftReference類來實(shí)現(xiàn)軟引用重父。

(3)弱引用(Weak Reference):它也是用來描述非須對象的花椭,但它的強(qiáng)度比軟引用更弱些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前房午。當(dāng)垃圾收集器工作時(shí)矿辽,無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對象郭厌。在JDK1.2之后袋倔,提供了WeakReference類來實(shí)現(xiàn)弱引用。

(4)虛引用(Phantom Reference):最弱的一種引用關(guān)系沪曙,完全不會(huì)對其生存時(shí)間構(gòu)成影響奕污,也無法通過虛引用來取得一個(gè)對象實(shí)例。為一個(gè)對象設(shè)置虛引用關(guān)聯(lián)的唯一目的是希望能在這個(gè)對象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知液走。JDK1.2之后提供了PhantomReference類來實(shí)現(xiàn)虛引用碳默。

二、判斷對象是否是垃圾的算法

引用計(jì)數(shù)算法:(老牌垃圾回收算法缘眶。無法處理循環(huán)引用嘱根,沒有被Java采納)

1.引用計(jì)數(shù)算法的概念:

引用計(jì)數(shù)是垃圾收集器中的早期策略。在這種方法中巷懈,堆中每個(gè)對象實(shí)例都有一個(gè)引用計(jì)數(shù)该抒。當(dāng)一個(gè)對象被創(chuàng)建時(shí),且將該對象實(shí)例分配給一個(gè)變量顶燕,該變量計(jì)數(shù)設(shè)置為1凑保。當(dāng)任何其它變量被賦值為這個(gè)對象的引用時(shí),計(jì)數(shù)加1(a = b,則b引用的對象實(shí)例的計(jì)數(shù)器+1)涌攻,但當(dāng)一個(gè)對象實(shí)例的某個(gè)引用超過了生命周期或者被設(shè)置為一個(gè)新值時(shí)欧引,對象實(shí)例的引用計(jì)數(shù)器減1。任何引用計(jì)數(shù)器為0的對象實(shí)例可以被當(dāng)作垃圾收集恳谎。當(dāng)一個(gè)對象實(shí)例被垃圾收集時(shí)芝此,它引用的任何對象實(shí)例的引用計(jì)數(shù)器減1。

2.優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 引用計(jì)數(shù)收集器可以很快的執(zhí)行因痛,交織在程序運(yùn)行中婚苹。對程序需要不被長時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利。

缺點(diǎn):

  • 引用和去引用伴隨加法和減法鸵膏,影響性能

  • 無法檢測出循環(huán)引用膊升。如父對象有一個(gè)對子對象的引用,子對象反過來引用父對象谭企。這樣廓译,他們的引用計(jì)數(shù)永遠(yuǎn)不可能為0结胀。

image

上面的3個(gè)圖中,對于最右邊的那張圖而言:循環(huán)引用的計(jì)數(shù)器都不為0责循,但是他們對于根對象都已經(jīng)不可達(dá)了,但是無法釋放攀操。

引用計(jì)數(shù)算法無法解決循環(huán)引用問題院仿,例如:

public class Object {

    Object field = null;

    public static void main(String[] args) {

        Thread thread = new Thread(new Runnable() {

            public void run() {

                Object objectA = new Object();

                Object objectB = new Object();//位置1

                objectA.field = objectB;

                objectB.field = objectA;//位置2

                //to do something

                objectA = null;

                objectB = null;//位置3

            }

        });

        thread.start();

        while (true);

    }

代碼解釋:

代碼中標(biāo)注了1、2速和、3三個(gè)數(shù)字歹垫,當(dāng)位置1的語句執(zhí)行完以后,兩個(gè)對象的引用計(jì)數(shù)全部為1颠放。當(dāng)位置2的語句執(zhí)行完以后排惨,兩個(gè)對象的引用計(jì)數(shù)就全部變成了2。當(dāng)位置3的語句執(zhí)行完以后碰凶,也就是將二者全部歸為空值以后暮芭,二者的引用計(jì)數(shù)仍然為1。根據(jù)引用計(jì)數(shù)算法的回收規(guī)則欲低,引用計(jì)數(shù)沒有歸0的時(shí)候是不會(huì)被回收的辕宏。

對于我們現(xiàn)在使用的GC來說,當(dāng)thread線程運(yùn)行結(jié)束后砾莱,會(huì)將objectA和objectB全部作為待回收的對象瑞筐。而如果我們的GC采用上面所說的引用計(jì)數(shù)算法,則這兩個(gè)對象永遠(yuǎn)不會(huì)被回收腊瑟,即便我們在使用后顯示的將對象歸為空值也毫無作用聚假。

根搜索算法

1.根搜索算法的概念:

首先了解一個(gè)概念:根集(Root Set)

所謂根集(Root Set)就是正在執(zhí)行的Java程序可以訪問的引用變量(注意:不是對象)的集合(包括局部變量、參數(shù)闰非、類變量)膘格,程序可以使用引用變量訪問對象的屬性和調(diào)用對象的方法。

這種算法的基本思路:

(1)通過一系列名為“GC Roots”的對象作為起始點(diǎn)河胎,尋找對應(yīng)的引用節(jié)點(diǎn)闯袒。
(2)找到這些引用節(jié)點(diǎn)后,從這些節(jié)點(diǎn)開始向下繼續(xù)尋找它們的引用節(jié)點(diǎn)游岳。
(3)重復(fù)(2)政敢。
(4)搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈相連時(shí)胚迫,就證明此對象是不可用的喷户。

Java和C#中都是采用根搜索算法來判定對象是否存活的。

2.標(biāo)記可達(dá)對象:

JVM中用到的所有現(xiàn)代GC算法在回收前都會(huì)先找出所有仍存活的對象访锻。根搜索算法是從離散數(shù)學(xué)中的圖論引入的褪尝,程序把所有的引用關(guān)系看作一張圖闹获。下圖3.0中所展示的JVM中的內(nèi)存布局可以用來很好地闡釋這一概念:

image

首先,垃圾回收器將某些特殊的對象定義為GC根對象河哑。所謂的GC根對象包括:

(1)虛擬機(jī)棧中引用的對象(棧幀中的本地變量表)避诽;

(2)方法區(qū)中的常量引用的對象;

(3)方法區(qū)中的類靜態(tài)屬性引用的對象璃谨;

(4)本地方法棧中JNI(Native方法)的引用對象沙庐。

(5)活躍線程。

接下來佳吞,垃圾回收器會(huì)對內(nèi)存中的整個(gè)對象圖進(jìn)行遍歷拱雏,它先從GC根對象開始,然后是根對象引用的其它對象底扳,比如實(shí)例變量铸抑。回收器將訪問到的所有對象都標(biāo)記為存活衷模。

存活對象在上圖中被標(biāo)記為藍(lán)色鹊汛。當(dāng)標(biāo)記階段完成了之后,所有的存活對象都已經(jīng)被標(biāo)記完了算芯。其它的那些(上圖中灰色的那些)也就是GC根對象不可達(dá)的對象柒昏,也就是說你的應(yīng)用不會(huì)再用到它們了。這些就是垃圾對象熙揍,回收器將會(huì)在接下來的階段中清除它們职祷。

關(guān)于標(biāo)記階段有幾個(gè)關(guān)鍵點(diǎn)是值得注意的:

1.開始進(jìn)行標(biāo)記前,會(huì)觸發(fā)一次Stop The World(STW)暫停届囚,會(huì)暫停全部線程有梆,否則如果對象圖一直在變化的話是無法真正去遍歷它的。

2.暫停時(shí)間的長短并不取決于堆內(nèi)對象的多少也不是堆的大小意系,而是存活對象的多少泥耀。因此,調(diào)高堆的大小并不會(huì)影響到標(biāo)記階段的時(shí)間長短蛔添。

3.在根搜索算法中痰催,要真正宣告一個(gè)對象死亡,至少要經(jīng)歷兩次標(biāo)記過程:

  • 如果對象在進(jìn)行根搜索后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈迎瞧,那它會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選夸溶。篩選的條件是此對象是否有必要執(zhí)行 finalize()方法(可看作析構(gòu)函數(shù),類似于OC中的dealloc凶硅,Swift中的deinit)缝裁。當(dāng)對象沒有覆蓋finalize()方法,或finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過足绅,虛擬機(jī)將這兩種情況都視為沒有必要執(zhí)行捷绑。

  • 如果該對象被判定為有必要執(zhí)行finalize()方法韩脑,那么這個(gè)對象將會(huì)被放置在一個(gè)名為F-Queue隊(duì)列中,并在稍后由一條由虛擬機(jī)自動(dòng)建立的粹污、低優(yōu)先級的Finalizer線程去執(zhí)行finalize()方法段多。finalize()方法是對象逃脫死亡命運(yùn)的最后一次機(jī)會(huì)(因?yàn)橐粋€(gè)對象的finalize()方法最多只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次),稍后GC將對F-Queue中的對象進(jìn)行第二次小規(guī)模的標(biāo)記壮吩,如果要在finalize()方法中成功拯救自己赡模,只要在finalize()方法中讓該對象重新引用鏈上的任何一個(gè)對象建立關(guān)聯(lián)即可伟姐。而如果對象這時(shí)還沒有關(guān)聯(lián)到任何鏈上的引用笑跛,那它就會(huì)被回收掉冤狡。

三生百、回收垃圾對象內(nèi)存的算法

在根搜索算法的基礎(chǔ)上递雀,現(xiàn)代虛擬機(jī)的實(shí)現(xiàn)當(dāng)中,垃圾搜集的算法主要有三種蚀浆,分別是標(biāo)記-清除算法缀程、復(fù)制算法、標(biāo)記-整理(標(biāo)記-清除-整理)算法市俊。這三種算法都擴(kuò)充了根搜索算法杨凑,不過它們理解起來還是非常好理解的。

復(fù)制算法(新生代的GC)

1.復(fù)制算法的概念:

將原有的內(nèi)存空間分為兩塊摆昧,每次只使用其中一塊撩满,在垃圾回收時(shí),將正在使用的內(nèi)存中的存活對象復(fù)制到未使用的內(nèi)存塊中绅你,之后伺帘,清除正在使用的內(nèi)存塊中的所有對象,交換兩個(gè)內(nèi)存的角色忌锯,完成垃圾回收伪嫁。

復(fù)制算法因?yàn)闀?huì)將存貨對象全部復(fù)制,如果執(zhí)行較多的復(fù)制操作偶垮,效率將會(huì)變低张咳,因此復(fù)制算法適合對象存活率較低的新生代

一種典型的基于Coping算法的垃圾回收是stop-and-copy算法,它將堆分成對象區(qū)和空閑區(qū)似舵,在對象區(qū)與空閑區(qū)的切換過程中脚猾,程序暫停執(zhí)行。

image
2.標(biāo)記-清除算法的缺點(diǎn):

優(yōu)點(diǎn):

  • 標(biāo)記階段和復(fù)制階段可以同時(shí)進(jìn)行啄枕。

  • 每次只對一塊內(nèi)存進(jìn)行回收婚陪,運(yùn)行高效。

  • 只需移動(dòng)棧頂指針频祝,按順序分配內(nèi)存即可泌参,實(shí)現(xiàn)簡單脆淹。

  • 內(nèi)存回收時(shí)不用考慮內(nèi)存碎片的出現(xiàn)(得活動(dòng)對象所占的內(nèi)存空間之間沒有空閑間隔)。

缺點(diǎn):

  • 需要一塊能容納下所有存活對象的額外的內(nèi)存空間沽一。因此盖溺,可一次性分配的最大內(nèi)存縮小了一半。

標(biāo)記-清除算法

1.標(biāo)記清除算法的概念:
image
  • 標(biāo)記-清除算法是現(xiàn)代垃圾回收算法的思想基礎(chǔ)铣缠。

  • 標(biāo)記-清除算法將垃圾回收分為兩個(gè)階段:標(biāo)記階段和清除階段烘嘱。首先在標(biāo)記階段通過前面的根搜索算法來標(biāo)記可達(dá)對象。因此蝗蛙,未被標(biāo)記的對象就是未被引用的垃圾對象蝇庭;然后,在清除階段捡硅,清除所有未被標(biāo)記的對象哮内。

2.標(biāo)記-清除算法詳解:

它的做法是當(dāng)堆中的有效內(nèi)存空間(available memory)被耗盡的時(shí)候,就會(huì)停止整個(gè)程序(也被成為stop the world)壮韭,然后進(jìn)行兩項(xiàng)工作北发,第一項(xiàng)則是標(biāo)記,第二項(xiàng)則是清除喷屋。

  • 標(biāo)記:標(biāo)記的過程其實(shí)就是琳拨,遍歷所有的GC Roots,然后將所有GC Roots可達(dá)的對象標(biāo)記為存活的對象屯曹。

  • 清除:清除的過程將遍歷堆中所有的對象狱庇,將沒有標(biāo)記的對象全部清除掉。

也就是說恶耽,就是當(dāng)程序運(yùn)行期間僵井,若可以使用的內(nèi)存被耗盡的時(shí)候,GC線程就會(huì)被觸發(fā)并將程序暫停驳棱,隨后將依舊存活的對象標(biāo)記一遍批什,最終再將堆中所有沒被標(biāo)記的對象全部清除掉,接下來便讓程序恢復(fù)運(yùn)行社搅。

3.標(biāo)記-清除算法的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

  • 不需要進(jìn)行對象的移動(dòng)驻债,并且僅對不存活的對象進(jìn)行處理,在存活對象比較多的情況下極為高效形葬。

缺點(diǎn):

  • 首先合呐,它的缺點(diǎn)就是效率比較低(遞歸與全堆對象遍歷),導(dǎo)致stop the world的時(shí)間比較長笙以,尤其對于交互式的應(yīng)用程序來說簡直是無法接受淌实。試想一下,如果你玩一個(gè)網(wǎng)站,這個(gè)網(wǎng)站一個(gè)小時(shí)就掛五分鐘拆祈,你還玩嗎恨闪?

  • 第二點(diǎn)主要的缺點(diǎn),則是這種方式清理出來的空閑內(nèi)存是不連續(xù)的放坏,這點(diǎn)不難理解咙咽,我們的死亡對象都是隨即的出現(xiàn)在內(nèi)存的各個(gè)角落的,現(xiàn)在把它們清除之后淤年,內(nèi)存的布局自然會(huì)亂七八糟钧敞。而為了應(yīng)付這一點(diǎn),JVM就不得不維持一個(gè)內(nèi)存的空閑列表麸粮,這又是一種開銷溉苛。而且在分配數(shù)組對象的時(shí)候,尋找連續(xù)的內(nèi)存空間會(huì)不太好找弄诲。

標(biāo)記—整理算法

1.標(biāo)記整理算法的概念:

標(biāo)記-整理算法采用 標(biāo)記-清除 算法一樣的方式進(jìn)行對象的標(biāo)記炊昆、清除,但在回收不存活的對象占用的空間后威根,會(huì)將所有存活的對象往左端空閑空間移動(dòng),并更新對應(yīng)的指針视乐。標(biāo)記-整理 算法是在標(biāo)記-清除 算法之上洛搀,又進(jìn)行了對象的移動(dòng)排序整理,因此成本更高佑淀,但卻解決了內(nèi)存碎片的問題留美。

2.標(biāo)記-清除算法的優(yōu)缺點(diǎn):
  • 標(biāo)記/整理算法不僅可以彌補(bǔ)標(biāo)記/清除算法當(dāng)中,內(nèi)存區(qū)域分散的缺點(diǎn)伸刃,也消除了復(fù)制算法當(dāng)中谎砾,內(nèi)存減半的高額代價(jià)。

  • 但是捧颅,標(biāo)記/整理算法唯一的缺點(diǎn)就是效率也不高景图。

  • 不僅要標(biāo)記所有存活對象,還要整理所有存活對象的引用地址碉哑。從效率上來說挚币,標(biāo)記/整理算法要低于復(fù)制算法。

分代收集算法:(新生代的GC+老年代的GC)

image

JVM為了優(yōu)化內(nèi)存的回收扣典,使用了分代回收的方式妆毕,對于新生代內(nèi)存的回收(Minor GC)主要采用復(fù)制算法。而對于老年代的回收(Major GC)贮尖,大多采用標(biāo)記-整理算法笛粘。

1.標(biāo)記-清除算法、復(fù)制算法、標(biāo)記整理算法的總結(jié):

三個(gè)算法都基于根搜索算法去判斷一個(gè)對象是否應(yīng)該被回收薪前,而支撐根搜索算法可以正常工作的理論依據(jù)润努,就是語法中變量作用域的相關(guān)內(nèi)容。因此序六,要想防止內(nèi)存泄露任连,最根本的辦法就是掌握好變量作用域,而不應(yīng)該使用C/C++式內(nèi)存管理方式例诀。

在GC線程開啟時(shí)随抠,或者說GC過程開始時(shí),它們都要暫停應(yīng)用程序(stop the world)繁涂。

它們的區(qū)別如下:(>表示前者要優(yōu)于后者拱她,=表示兩者效果一樣)

(1)效率:復(fù)制算法 > 標(biāo)記/整理算法 > 標(biāo)記/清除算法(此處的效率只是簡單的對比時(shí)間復(fù)雜度,實(shí)際情況不一定如此)扔罪。

(2)內(nèi)存整齊度:復(fù)制算法=標(biāo)記/整理算法>標(biāo)記/清除算法秉沼。

(3)內(nèi)存利用率:標(biāo)記/整理算法=標(biāo)記/清除算法>復(fù)制算法。

注意事項(xiàng):

  • 可以看到標(biāo)記/清除算法是比較落后的算法了矿酵,但是后兩種算法卻是在此基礎(chǔ)上建立的唬复。

  • 時(shí)間與空間不可兼得。

當(dāng)前商業(yè)虛擬機(jī)的GC都是采用的“分代收集算法”全肮,這并不是什么新的思想敞咧,只是根據(jù)對象的存活周期的不同將內(nèi)存劃分為幾塊兒,根據(jù)上面三種算法的選取應(yīng)用辜腺。一般是把Java堆分為新生代和老年代:短命對象歸為新生代休建,長命對象歸為老年代。

  • 少量對象存活评疗,適合復(fù)制算法:在新生代中测砂,每次GC時(shí)都發(fā)現(xiàn)有大批對象死去,只有少量存活百匆,那就選用復(fù)制算法砌些,只需要付出少量存活對象的復(fù)制成本就可以完成GC。

  • 大量對象存活加匈,適合用標(biāo)記-清理/標(biāo)記-整理:在老年代中寄症,因?yàn)閷ο蟠婊盥矢摺]有額外空間對他進(jìn)行分配擔(dān)保矩动,就必須使用“標(biāo)記-清理”/“標(biāo)記-整理”算法進(jìn)行GC有巧。

注:老年代的對象中,有一小部分是因?yàn)樵谛律厥諘r(shí)悲没,老年代做擔(dān)保篮迎,進(jìn)來的對象男图;絕大部分對象是因?yàn)楹芏啻蜧C都沒有被回收掉而進(jìn)入老年代。

2.下邊將會(huì)講解它們?nèi)绾喂ぷ鞯?/h5>
第一次GC:

在不斷創(chuàng)建對象的過程中甜橱,當(dāng)Eden區(qū)域被占滿逊笆,此時(shí)會(huì)開始做Young GC也叫Minor GC

1)第一次GC時(shí)Survivous中S0區(qū)和S1區(qū)都為空,將其中一個(gè)作為To Survivous(用來存儲Eden區(qū)域執(zhí)行GC后不能被回收的對象)岂傲。比如:將S0作為To Survivous难裆,則S1為From Survivous。

2)將Eden區(qū)域經(jīng)過GC不能被回收的對象存儲到To Survivous(S0)區(qū)域(此時(shí)Eden區(qū)域的內(nèi)存會(huì)在垃圾回收的過程中全部釋放)镊掖,但如果To Survivous(S0)被占滿了乃戈,Eden中剩下不能被回收對象只能存放到Old區(qū)域。

3)將Eden區(qū)域空間清空亩进,此時(shí)From Survivous區(qū)域(S1)也是空的症虑。

4)S0與S1互相切換標(biāo)簽,S0為From Survivous谍憔,S1為To Survivous。

image
第二次GC:

當(dāng)?shù)诙蜤den區(qū)域被占滿時(shí)主籍,此時(shí)開始做GC

1)將Eden和From Survivous(S0)中經(jīng)過GC未被回收的對象遷移到To Survivous(S1)习贫,如果To Survious(S1)區(qū)放不下,將剩下的不能回收對象放入Old區(qū)域千元;

2)將Eden區(qū)域空間和From Survivous(S0)區(qū)域空間清空苫昌;

3)S0與S1互相切換標(biāo)簽,S0為To Survivous诅炉,S1為From Survivous。

image

第三次屋厘,第四次一次類推涕烧,始終保證S0和S1有一個(gè)空的,用來存儲臨時(shí)對象汗洒,用于交換空間的目的议纯。反反復(fù)復(fù)多次沒有被淘汰的對象,將會(huì)被放入Old區(qū)域中溢谤,默認(rèn)15次(由參數(shù)--XX:MaxTenuringThreshold=15 決定)瞻凤。

什么情況下對象會(huì)從新生代進(jìn)入老年代?
  • 一定次數(shù)的minor gc后

常規(guī)對象被創(chuàng)建之后是存儲在年輕代的Eden區(qū)世杀,每一個(gè)對象都有年齡阀参,在YGC后,survivor1區(qū)還存活的對象的年齡全部+1瞻坝,當(dāng)對象年齡達(dá)到15時(shí)蛛壳,被移交到老年代,15是系統(tǒng)默認(rèn)的,我們可以通過JVM參數(shù)-XX:MaxTenuringThreshold來設(shè)置

  • minor gc后survivor放不下

在MinorGC之后存活的對象超過了survivor區(qū)的大小衙荐,會(huì)將這些對象直接轉(zhuǎn)移到老年代

  • 動(dòng)態(tài)對象年齡判斷

如果再survivor區(qū)捞挥,有某一年齡的對象的總大小超過了survivor區(qū)大小的50%,年齡大于或等于該年齡的對象就可以直接進(jìn)入老年代

  • 大對象

所謂大對象就是指需要比較大的連續(xù)內(nèi)存空間的Java對象忧吟,比如很長的字符串或數(shù)組溜族,我們可以通過JVM參數(shù)-XX:PretenureSizeThreshold指定大對象的容量,單位是字節(jié)劣像。

詳情參考:https://www.pianshen.com/article/24491623364/

四耳奕、垃圾回收的類型

Minor GC屋群、Major GC、Full GC降狠、Mixed GC

1.Minor GC:

Minor GC是發(fā)生在新生代中的垃圾收集動(dòng)作榜配,采用的是復(fù)制算法临燃∧だ龋 

對象在Eden和From區(qū)出生后爪瓜,在經(jīng)過一次Minor GC后炬转,如果對象還存活算灸,并且能夠被to區(qū)所容納荐吵,那么在使用復(fù)制算法時(shí)這些存活對象就會(huì)被復(fù)制到to區(qū)域先煎,然后清理掉Eden區(qū)和from區(qū)薯蝎,并將這些對象的年齡設(shè)置為1,以后對象在Survivor區(qū)每熬過一次Minor GC缩筛,就將對象的年齡+1艺演,當(dāng)對象的年齡達(dá)到某個(gè)值時(shí)(默認(rèn)是15歲胎撤,可以通過參數(shù) --XX:MaxTenuringThreshold設(shè)置)伤提,這些對象就會(huì)成為老年代识藤。

但這也是不一定的,對于一些較大的對象(即需要分配一塊較大的連續(xù)內(nèi)存空間)則是直接進(jìn)入老年代

2.Major GC:

CMS收集器中稽穆,當(dāng)老年代滿時(shí)會(huì)觸發(fā) Major GC柱彻〔驼停 

目前卖擅,只有CMS收集器會(huì)有單獨(dú)收集老年代的行為惩阶。其他收集器均無此行為≌副穑 

3.Full GC

Full GC 對收集整堆(新生代账千、老年代)和方法區(qū)的垃圾收集匀奏。

  • 當(dāng)年老代滿時(shí)會(huì)引發(fā)Full GC,F(xiàn)ull GC將會(huì)同時(shí)回收新生代聚磺、年老代 瘫寝;

  • 當(dāng)永久代滿時(shí)也會(huì)引發(fā)Full GC,會(huì)導(dǎo)致Class暮屡、Method元信息的卸載 准夷。

  • 調(diào)用System.gc時(shí)衫嵌,系統(tǒng)建議執(zhí)行Full GC,但是不一定會(huì)執(zhí)行 墓律。

  • 老年代空間不足

  • 方法區(qū)空間不足

  • 通過 Minor GC 后進(jìn)入老年代的空間大于老年代的可用內(nèi)存

  • 由Eden區(qū)、survivor space1(From Space)區(qū)向survivor space2(To Space)區(qū)復(fù)制時(shí)耻讽,對象大小大于To Space可用內(nèi)存针肥,則把該對象轉(zhuǎn)存到老年代即纲,且老年代的可用內(nèi)存小于該對象大小 蜂厅。

4.Mixed GC

混合GC收集整個(gè)新生代 和部分老年代的垃圾手機(jī)(目前僅G1 GC 收集器支持)

總結(jié)
  • Minor GC 是 清理 新生代中的Eden區(qū)掘猿,Survivor區(qū)滿時(shí)不會(huì)觸發(fā) ;

  • Major GC 是 清理 老年代 唇跨;

  • Full GC 是 清理整個(gè)堆和方法區(qū)稠通,包括 年輕代、老年代和方法區(qū)买猖。

  • 都會(huì) 觸發(fā)STW

五改橘、垃圾回收器簡介

需要注意的是,每一個(gè)回收器都存在Stop The World 的問題政勃,只不過各個(gè)回收器在Stop The World 時(shí)間優(yōu)化程度唧龄、算法的不同奸远,可根據(jù)自身需求選擇適合的回收器胖烛。

1.Serial(-XX:+UseSerialGC)

從名字我們可以看出趟畏,這是一個(gè)串行收集器

Serial收集器是Java虛擬機(jī)中最基本、歷史最悠久的收集器展鸡。在JDK1.3之前是Java虛擬機(jī)新生代收集器的唯一選擇。目前也是ClientVM下ServerVM 4核4GB以下機(jī)器默認(rèn)垃圾回收器。Serial收集器并不是只能使用一個(gè)CPU進(jìn)行收集吧彪,而是當(dāng)JVM需要進(jìn)行垃圾回收的時(shí)候那先,需暫停所有的用戶線程勋又,直到回收結(jié)束祟牲。

使用算法:復(fù)制算法

image

JVM中文名稱為Java虛擬機(jī)运杭,因此它像一臺虛擬的電腦在工作,而其中的每一個(gè)線程都被認(rèn)為是JVM的一個(gè)處理器,因此圖中的CPU0园欣、CPU1實(shí)際上為用戶的線程,而不是真正的機(jī)器CPU,不要誤解哦。

Serial收集器雖然是最老的宣脉,但是它對于限定單個(gè)CPU的環(huán)境來說,由于沒有線程交互的開銷,專心做垃圾收集践险,所以它在這種情況下是相對于其他收集器中最高效的。

2.SerialOld(-XX:+UseSerialGC)**

SerialOld是Serial收集器的老年代收集器版本,它同樣是一個(gè)單線程收集器奶镶,這個(gè)收集器目前主要用于Client模式下使用厂镇。如果在Server模式下,它主要還有兩大用途:一個(gè)是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用左刽,另外一個(gè)就是作為CMS收集器的后備預(yù)案捺信,如果CMS出現(xiàn)Concurrent Mode Failure,則SerialOld將作為后備收集器欠痴。

使用算法:標(biāo)記 - 整理算法

運(yùn)行示意圖與上圖一致迄靠。

3.ParNew(-XX:+UseParNewGC)

ParNew其實(shí)就是Serial收集器的多線程版本。除了Serial收集器外喇辽,只有它能與CMS收集器配合工作掌挚。

使用算法:復(fù)制算法

image

ParNew是許多運(yùn)行在Server模式下的JVM首選的新生代收集器。但是在單CPU的情況下菩咨,它的效率遠(yuǎn)遠(yuǎn)低于Serial收集器吠式,所以一定要注意使用場景陡厘。

4.ParallelScavenge(-XX:+UseParallelGC)(JDK8默認(rèn))

ParallelScavenge又被稱為吞吐量優(yōu)先收集器,和ParNew 收集器類似特占,是一個(gè)新生代收集器糙置。

使用算法:復(fù)制算法

ParallelScavenge收集器的目標(biāo)是達(dá)到一個(gè)可控件的吞吐量,所謂吞吐量就是CPU用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值是目,即吞吐量 = 運(yùn)行用戶代碼時(shí)間 / (運(yùn)行用戶代碼時(shí)間 + 垃圾收集時(shí)間)谤饭。如果虛擬機(jī)總共運(yùn)行了100分鐘,其中垃圾收集花了1分鐘懊纳,那么吞吐量就是99% 揉抵。

5.ParallelOld(-XX:+UseParallelOldGC)(JDK8默認(rèn))

ParallelOld是并行收集器,和SerialOld一樣长踊,ParallelOld是一個(gè)老年代收集器功舀,是老年代吞吐量優(yōu)先的一個(gè)收集器萍倡。這個(gè)收集器在JDK1.6之后才開始提供的身弊,在此之前,ParallelScavenge只能選擇SerialOld來作為其老年代的收集器列敲,這嚴(yán)重拖累了ParallelScavenge整體的速度阱佛。而ParallelOld的出現(xiàn)后,“吞吐量優(yōu)先”收集器才名副其實(shí)戴而!

使用算法:標(biāo)記 - 整理算法

image

在注重吞吐量與CPU數(shù)量大于1的情況下凑术,都可以優(yōu)先考慮ParallelScavenge + ParalleloOld收集器。

6.CMS (-XX:+UseConcMarkSweepGC)

CMS是一個(gè)老年代收集器所意,全稱 Concurrent Low Pause Collector淮逊,是JDK1.4后期開始引用的新GC收集器,在JDK1.5扶踊、1.6中得到了進(jìn)一步的改進(jìn)泄鹏。它是對于響應(yīng)時(shí)間的重要性需求大于吞吐量要求的收集器。對于要求服務(wù)器響應(yīng)速度高的情況下秧耗,使用CMS非常合適备籽。

CMS的一大特點(diǎn),就是用兩次短暫的暫停來代替串行或并行標(biāo)記整理算法時(shí)候的長暫停分井。

使用算法:標(biāo)記 - 清理

CMS的執(zhí)行過程如下:
  • 初始標(biāo)記(STW initial mark)

在這個(gè)階段车猬,需要虛擬機(jī)停頓正在執(zhí)行的應(yīng)用線程,官方的叫法STW(Stop Tow World)尺锚。這個(gè)過程從根對象掃描直接關(guān)聯(lián)的對象珠闰,并作標(biāo)記。這個(gè)過程會(huì)很快的完成瘫辩。

  • 并發(fā)標(biāo)記(Concurrent marking)

這個(gè)階段緊隨初始標(biāo)記階段铸磅,在“初始標(biāo)記”的基礎(chǔ)上繼續(xù)向下追溯標(biāo)記赡矢。注意這里是并發(fā)標(biāo)記,表示用戶線程可以和GC線程一起并發(fā)執(zhí)行阅仔,這個(gè)階段不會(huì)暫停用戶的線程哦吹散。

  • 并發(fā)預(yù)清理(Concurrent precleaning)

這個(gè)階段任然是并發(fā)的,JVM查找正在執(zhí)行“并發(fā)標(biāo)記”階段時(shí)候進(jìn)入老年代的對象(可能這時(shí)會(huì)有對象從新生代晉升到老年代八酒,或被分配到老年代)空民。通過重新掃描,減少在一個(gè)階段“重新標(biāo)記”的工作羞迷,因?yàn)橄乱浑A段會(huì)STW界轩。

  • 重新標(biāo)記(STW remark)

這個(gè)階段會(huì)再次暫停正在執(zhí)行的應(yīng)用線程,重新重根對象開始查找并標(biāo)記并發(fā)階段遺漏的對象(在并發(fā)標(biāo)記階段結(jié)束后對象狀態(tài)的更新導(dǎo)致)衔瓮,并處理對象關(guān)聯(lián)浊猾。這一次耗時(shí)會(huì)比“初始標(biāo)記”更長,并且這個(gè)階段可以并行標(biāo)記热鞍。

  • 并發(fā)清理(Concurrent sweeping)

這個(gè)階段是并發(fā)的葫慎,應(yīng)用線程和GC清除線程可以一起并發(fā)執(zhí)行。

  • 并發(fā)重置(Concurrent reset)

這個(gè)階段任然是并發(fā)的薇宠,重置CMS收集器的數(shù)據(jù)結(jié)構(gòu)偷办,等待下一次垃圾回收。

CMS的缺點(diǎn):

1.內(nèi)存碎片澄港。由于使用了 標(biāo)記-清理 算法椒涯,導(dǎo)致內(nèi)存空間中會(huì)產(chǎn)生內(nèi)存碎片。不過CMS收集器做了一些小的優(yōu)化回梧,就是把未分配的空間匯總成一個(gè)列表废岂,當(dāng)有JVM需要分配內(nèi)存空間的時(shí)候,會(huì)搜索這個(gè)列表找到符合條件的空間來存儲這個(gè)對象狱意。但是內(nèi)存碎片的問題依然存在湖苞,如果一個(gè)對象需要3塊連續(xù)的空間來存儲,因?yàn)閮?nèi)存碎片的原因髓涯,尋找不到這樣的空間袒啼,就會(huì)導(dǎo)致Full GC。

2.需要更多的CPU資源纬纪。由于使用了并發(fā)處理蚓再,很多情況下都是GC線程和應(yīng)用線程并發(fā)執(zhí)行的,這樣就需要占用更多的CPU資源包各,也是犧牲了一定吞吐量的原因摘仅。

3.需要更大的堆空間。因?yàn)镃MS標(biāo)記階段應(yīng)用程序的線程還是執(zhí)行的问畅,那么就會(huì)有堆空間繼續(xù)分配的問題娃属,為了保障CMS在回收堆空間之前還有空間分配給新加入的對象六荒,必須預(yù)留一部分空間。CMS默認(rèn)在老年代空間使用68%時(shí)候啟動(dòng)垃圾回收矾端√突鳎可以通過-XX:CMSinitiatingOccupancyFraction=n來設(shè)置這個(gè)閥值。

7.GarbageFirst(G1)

這是一個(gè)新的垃圾回收器秩铆,既可以回收新生代也可以回收老年代砚亭,SunHotSpot1.6u14以上EarlyAccess版本加入了這個(gè)回收器,Sun公司預(yù)期SunHotSpot1.7發(fā)布正式版本殴玛。通過重新劃分內(nèi)存區(qū)域捅膘,整合優(yōu)化CMS,同時(shí)注重吞吐量和響應(yīng)時(shí)間滚粟。杯具的是Oracle收購這個(gè)收集器之后將其用于商用收費(fèi)版收集器寻仗。因此目前暫時(shí)沒有發(fā)現(xiàn)哪個(gè)公司使用它,這個(gè)放在之后再去研究吧凡壤。


整理一下新生代和老年代的收集器署尤。

新生代收集器:
  • Serial (-XX:+UseSerialGC)

  • ParNew(-XX:+UseParNewGC)

  • ParallelScavenge(-XX:+UseParallelGC)

  • G1 收集器

老年代收集器:
  • SerialOld(-XX:+UseSerialOldGC)

  • ParallelOld(-XX:+UseParallelOldGC)

  • CMS(-XX:+UseConcMarkSweepGC)

  • G1 收集器

目前了解的GC收集器就是這么多了,如果有哪位有緣的朋友看到了這篇文章鲤遥,恰好有更好的GC收集器推薦沐寺,歡迎留言交流林艘。

參考鏈接:http://www.reibang.com/p/5261a62e4d29

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盖奈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狐援,更是在濱河造成了極大的恐慌钢坦,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啥酱,死亡現(xiàn)場離奇詭異爹凹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)镶殷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門禾酱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绘趋,你說我怎么就攤上這事颤陶。” “怎么了陷遮?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵滓走,是天一觀的道長。 經(jīng)常有香客問我帽馋,道長搅方,這世上最難降的妖魔是什么比吭? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮姨涡,結(jié)果婚禮上衩藤,老公的妹妹穿的比我還像新娘。我一直安慰自己涛漂,他們只是感情好慷彤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怖喻,像睡著了一般底哗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锚沸,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天跋选,我揣著相機(jī)與錄音,去河邊找鬼哗蜈。 笑死前标,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的距潘。 我是一名探鬼主播炼列,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼音比!你這毒婦竟也來了俭尖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤洞翩,失蹤者是張志新(化名)和其女友劉穎稽犁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骚亿,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡已亥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了来屠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虑椎。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俱笛,靈堂內(nèi)的尸體忽然破棺而出捆姜,到底是詐尸還是另有隱情,我是刑警寧澤嫂粟,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布娇未,位于F島的核電站,受9級特大地震影響星虹,放射性物質(zhì)發(fā)生泄漏零抬。R本人自食惡果不足惜镊讼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望平夜。 院中可真熱鬧蝶棋,春花似錦、人聲如沸忽妒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽段直。三九已至吃溅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸯檬,已是汗流浹背决侈。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喧务,地道東北人赖歌。 一個(gè)月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像功茴,于是被迫代替她去往敵國和親庐冯。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容