GC算法及收集器
1 GC的概念
垃圾收集 Garbage Collection 通常被稱為“GC”惹谐,它誕生于1960年 MIT 的 Lisp 語言晚凿,經(jīng)過半個(gè)多世紀(jì),目前已經(jīng)十分成熟了帅刊。
jvm 中全闷,程序計(jì)數(shù)器绩郎、虛擬機(jī)棧、本地方法棧都是線程私有的翁逞,它們隨線程而生肋杖,隨線程而滅。棧幀隨著方法的進(jìn)入和退出做入棧和出棧操作挖函,實(shí)現(xiàn)了自動(dòng)的內(nèi)存清理状植。
因此,我們的內(nèi)存垃圾回收主要集中于 java 堆和方法區(qū)中挪圾,在程序運(yùn)行期間浅萧,這部分內(nèi)存的分配和使用都是動(dòng)態(tài)的.
2 對(duì)象存活判斷
判斷對(duì)象是否存活一般有兩種方式:
引用計(jì)數(shù): 每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1哲思,引用釋放時(shí)計(jì)數(shù)減1洼畅,計(jì)數(shù)為0時(shí)可以回收。此方法簡單棚赔,但是無法解決對(duì)象相互循環(huán)引用的問題帝簇。
可達(dá)性分析(Reachability Analysis): 從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈靠益。當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí)丧肴,則證明此對(duì)象是不可用的。不可達(dá)對(duì)象胧后。
在java中芋浮,可作為GC Roots的對(duì)象有:
1.虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象;
2.方法區(qū)中的類靜態(tài)屬性引用的對(duì)象壳快;
3.方法區(qū)中常量引用的對(duì)象纸巷;
4.本地方法棧中JNI(即一般說的Native方法)中引用的對(duì)象
關(guān)于GC Roots具體說明(建議看下)
3 垃圾收集算法
3.1 標(biāo)記清除(Mark-Sweep)
“標(biāo)記-清除”(Mark-Sweep)
算法分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象(引用計(jì)數(shù)法或者可達(dá)性分析)瘤旨,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。
它是最基礎(chǔ)的收集算法竖伯,后續(xù)的收集算法都是基于這種思路并對(duì)其缺點(diǎn)進(jìn)行改進(jìn)而得到的存哲。缺點(diǎn):
它的主要缺點(diǎn)有兩個(gè):一個(gè)是效率問題,標(biāo)記和清除過程的效率都不高七婴;另外一個(gè)是空間問題祟偷,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致打厘,當(dāng)程序在以后的運(yùn)行過程中需要分配較大對(duì)象時(shí)無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作修肠。
3.2 復(fù)制(Copying)
-
“復(fù)制”(Copying)
復(fù)制算法在標(biāo)記清除算法的基礎(chǔ)上,針對(duì)內(nèi)存碎片問題做了一下優(yōu)化婚惫,此算法把內(nèi)存分為大小相同的兩塊氛赐,每次在使用的時(shí)候只使用其中的一塊。
當(dāng)一塊內(nèi)存用完的時(shí)候先舷。把存活對(duì)象復(fù)制到另外的一塊中艰管,然后清除當(dāng)前這塊中的所有的對(duì)象,如此反復(fù)蒋川。 -
缺點(diǎn):
使用當(dāng)前算法牲芋,解決了內(nèi)存碎片化嚴(yán)重的問題,但是存在缺陷就是每次只使用一般的空間捺球,空間利用率受到影響缸浦。同時(shí)對(duì)于存活周期長的對(duì)象,復(fù)制次數(shù)多氮兵。
3.3 標(biāo)記整理(Mark-Compact)
復(fù)制收集算法在對(duì)象存活率較高時(shí)就要執(zhí)行較多的復(fù)制操作裂逐,效率將會(huì)變低。更關(guān)鍵的是泣栈,如果不想浪費(fèi)50%的空間卜高,就需要有額外的空間進(jìn)行分配擔(dān)保,
以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況南片,所以在老年代一般不能直接選用這種算法掺涛。
根據(jù)老年代的特點(diǎn),有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法疼进,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣薪缆,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,
而是讓所有存活的對(duì)象都向一端移動(dòng)伞广,然后直接清理掉端邊界以外的內(nèi)存
3.4 分代收集算法(Generational Collection)
GC分代的基于一個(gè)假設(shè):絕大部分對(duì)象的生命周期都非常短暫拣帽,存活時(shí)間短。
“分代收集”(Generational Collection)算法赔癌,把Java堆分為新生代和老年代诞外,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
- 新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去灾票,只有少量存活峡谊,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集刊苍。
- 老年代中因?yàn)閷?duì)象存活率高既们、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法來進(jìn)行回收正什。
4 垃圾收器及常用組合
-
新生代
- Serial收集器(單線程)
- ParNew收集器(多線程)
- Parallel Scavenge收集器
-
老年代
- Serial Old(MSC)收集器
- Parallel Old收集器
- CMS(Concurrent Mark Sweep)收集器
-
新老年代都適合
- G1收集器
參考:常見JVM垃圾收集器一覽
參考鏈接