Java中GC(垃圾回收)算法

github上的地址:DevelopBlog

與C語言不同懂酱,Java內(nèi)存(堆內(nèi)存)的回收由JVM垃圾收集器自動完成,不需要程序開發(fā)者手動釋放內(nèi)存曙求。

從Java內(nèi)存模型(鏈接)一文中,我們知道,java中幾乎所有的對象實例存儲在堆內(nèi)存中聘鳞,故而堆內(nèi)存是JVM垃圾回收的主要陣地。

哪些對象需要被回收要拂?

在討論GC之前我們需要考慮一個問題抠璃?如何確定一個對象是否需要被回收?

兩個方法:引用計數(shù)法可達性分析法脱惰。

引用計數(shù)法

給對象添加一個引用計數(shù)器搏嗡,每當有一個地方引用它時,計數(shù)器+1拉一,當引用失效時采盒,計數(shù)器-1;當計數(shù)器為0舅踪,則表明它沒有被引用纽甘,也就是說可以被GC回收。

優(yōu)點:此方法簡單粗暴抽碌,效率很高悍赢。很多其他語言也是用這一方法進行對象的回收判斷决瞳。

缺點:此方法無法解決對象之間的相互循環(huán)引用問題。例如:

public class GCVinctorTest {

    private Object instance = null;
    
    public static void main(String[] args) {
        GCVinctorTest objA = new GCVinctorTest();
        GCVinctorTest objB = new GCVinctorTest();

        objA.instance = objB;
        objB.instance = objA;

        objA = null;
        objB = null;

        System.gc();
    }
}
image.png
  • step1:實例A的引用計數(shù)加1左权,實例A的引用計數(shù)=1皮胡;
  • step2:實例B的引用計數(shù)加1,實例B的引用計數(shù)=1赏迟;
  • step3:實例A的引用計數(shù)加1屡贺,實例A的引用計數(shù)=2;
  • step4:實例B的引用計數(shù)加1锌杀,實例B的引用計數(shù)=2甩栈;

執(zhí)行

        objA = null;
        objB = null;

之后:

  • 實例A的引用計數(shù)減1,實例A的引用計數(shù)=1糕再;
  • 實例B的引用計數(shù)減1枉长,實例B的引用計數(shù)=1离熏;

此時开镣,objA與objB相互引用哗咆,他們的引用計數(shù)器都不是0,除此之外猾担,再無任何其他實際引用袭灯,但是引用計數(shù)法無法通知GC收集器回收他們。

可達性分析算法

可達性分析算法 通過一系列的被稱為GC Roots的對象愛你個作為起始點(相當于根)绑嘹,其他對象(相當于樹枝或樹葉)直接或間接都與這個GC Roots相連稽荧。如果一個對象與root不相連,則就說明這個對象是不可用的圾叼,GC就可以將它回收蛤克。如圖所示

image.png

圖中obj1,obj2,obj3都與roots有直接或間接關聯(lián),不可被回收的存活對象夷蚊,obj6,obj4髓介,obj5雖然這三者之間有關聯(lián)惕鼓,但是與roots已經(jīng)斷開,故而可被回收對象唐础。

HotSpot中箱歧,JVM使用OopMap的數(shù)據(jù)結構來記錄對象內(nèi)什么偏移量存儲的是什么類型的數(shù)據(jù)的映射關系,在JIT編譯過程中一膨,也會在特定位置記錄下棧和寄存器中的那些位置和引用的呀邢,這樣GC在掃描時就可以直接獲得這些信息,而不需要去遍歷GC roots的引用鏈豹绪,提高了回收效率价淌。

HotSpot中,使用可達性分析算法進行可回收對象的標記。

GC ROOTS分類:

* 虛擬機棧(棧幀中的本地變量表)中引用的對象
* 方法區(qū)中類靜態(tài)屬性引用的對象
* 方法區(qū)中常量引用的對象
* 本地方法棧中JNI(Natice方法)中引用的對象

(finalize()方法不討論)

image.png

從從上圖看出蝉衣,存在3個GC Root:

靜態(tài)變量RefV1指向堆中實例1括尸;

局部變量RefV2指向堆中實例2;

Jni變量RefV3指向堆中實例3病毡,實例3指向了實例4濒翻;

故而,實例1啦膜,2有送,3,4都是可以存活的對象僧家;

而實例5不存在GC Root雀摘,故實例5可以被回收,雖然實例6被實例5引用啸臀,但是實例5沒有Gc Root(整條鏈無Root)届宠,故實例6也可以被回收。

引用的四種類型:

從強到弱分為:強引用乘粒,軟引用(soft)豌注,弱引用(weak),虛引用

  • 強引用:在開發(fā)中經(jīng)常的寫法灯萍,類似于BeanDemo demo=new BeanDemo();這種寫法轧铁,極為強引用。只要這種引用還存在旦棉,該實例就不會被標記為可回收齿风,垃圾回收器也就不會回收掉該對象。
  • 軟引用:用來引用一些有用但是非必需的對象绑洛。在系統(tǒng)將要發(fā)生內(nèi)存溢出OOM時救斑,有這種引用的對象,將要被回收真屯。
  • 弱引用:用來引用一些有用但是非必需的對象脸候。但是與軟引用在OOM進行回收不同,有這些引用的對象绑蔫,只要發(fā)生垃圾回收运沦,該對象將被回收。
  • 虛引用:無法通過虛引用獲得一個對象的實例配深,設置虛引用的目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知携添。

垃圾回收算法

標記—清除算法(mark-and-sweep)

顧名思義:先標記后清除(廢話)。JVM首先標記出所有需要回收的對象篓叶,在標記完成之后烈掠,在下一次垃圾回收的時候統(tǒng)一進行回收這些對象羞秤。
如圖所示:

清除前

清除后

可以看到,垃圾回收之后向叉,剩余的存貨對象分布比較雜亂锥腻,產(chǎn)生大量的碎片。碎片如果太多母谎,將會導致瘦黑,內(nèi)存空間的不連續(xù)性,如果這時出現(xiàn)一個大對象(如數(shù)組)奇唤,將會無法為其分配空間幸斥。

復制算法(Copying)

為了解決標記—清除算法的弊端,出現(xiàn)了復制算法咬扇。該算法首先將內(nèi)存空間分為兩部分甲葬,一次只使用其中的一塊。當GC發(fā)生之時懈贺,對象被清理之后经窖,將剩余的存活對象復制到另一部分內(nèi)存空間上,再把當前的內(nèi)存空間清空梭灿,這樣就解決了碎片過多的問題画侣。如圖所示:

此方法雖然解決了碎片過多的問題,但是另一個顯著問題又出現(xiàn)了:內(nèi)存利用率太低堡妒!

在同一時間配乱,只有一部分的內(nèi)存在被使用,如果對象存活率比較高的時候皮迟,復制算法將會進行較高的復制操作搬泥,復制算法將會非常低》幔考慮上篇文章的老年代與新生代的各自不同特點忿檩,可見此算法不適用于老年代。

標記—整理算法

為了解決復制算法的利用率低的問題爆阶,提出了標記—整理算法算法休溶,與標記—清除算法一樣,首先對對象進行標記扰她,但后續(xù)步驟則不是對可回收對象進行清理,而是讓存活的對象向內(nèi)存空間的一端就行移動芭碍,然后清理掉端邊界以外的內(nèi)存徒役。

分代收集

這是上篇文章已經(jīng)提及的,根據(jù)對象的存活周期將內(nèi)存劃分為新生代與老年代窖壕。然后根據(jù)各個年代不同的特點采用最適合的手機算法忧勿。

例如:

  • 新生代的對象朝生夕死杉女,時時產(chǎn)生新對象,時時又會有大批對象死去鸳吸,這個區(qū)域就可以使用復制算法熏挎;
  • 老年代因為對象存活率特別高,不容易被回收晌砾,需要使用標記—清除標記—整理算法進行回收坎拐。

上文中提到,在GC過程中使用OopMap記錄對象 的偏移和類型信息养匈,隨著程序的執(zhí)行哼勇,無時不刻都有可能會產(chǎn)生新的對象,如果每進行一步操作呕乎,都生成相應的OopMap积担,那會需要大量的額外空間,GC成本變的非常高猬仁。在實際的HotSpot中帝璧,JVM只在程序執(zhí)行到特定的位置才生成OopMap,這些位置稱為“安全點”湿刽。

當程序執(zhí)行到安全點之后的烁,由于需要進行GC ROOTS統(tǒng)計,需要暫停進程中所有的線程叭爱,如果不暫停撮躁,就會在統(tǒng)計的過程中不斷產(chǎn)生的新的對象,使得統(tǒng)計無法得到準確的結果买雾。

安全點的選定既不能太少把曼,會導致GC等待時間太長;又不能太多漓穿,導致太過于頻繁而增大運行負荷嗤军。故而選定的標準為:是否有讓程序城市間執(zhí)行的特征。一般最明顯的就是制定序列的服用(指令將在以后介紹)晃危,如:方法調(diào)用叙赚,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)等僚饭。

線程如何達到安全點呢震叮?JVM在安全點的地方設置一個中斷標志,當線程執(zhí)行到這個標志時鳍鸵,線程自己中斷掛起苇瓣。

這里還有一個問題,當一個線程沒有處于運行狀態(tài)的時候偿乖,如SLeep或者Blocked狀態(tài)击罪,那么如何才能中斷自身呢哲嘲?GC也不可能等待該線程重新?lián)屨糃PU再進行中斷,這時安全區(qū)域的概念產(chǎn)生了媳禁。安全區(qū)域同安全點的概念一樣眠副,只不過安全區(qū)域是一段代碼片段。

上文提過竣稽,為了保證GC ROOTS統(tǒng)計的完整性囱怕,統(tǒng)計時產(chǎn)生新的引用關系,才提出的安全點這一概念丧枪。同樣光涂,我們選定安全區(qū)域的標準也是該段代碼并不會產(chǎn)生新的引用關系

在線程執(zhí)行到安全區(qū)域中的代碼時拧烦,首先標記一下自己已經(jīng)進入了安全地帶了忘闻,這樣JVM進行GC時,就不用考慮該線程的引用關系了恋博。當線程將要離開該安全區(qū)域的時候齐佳,該線程首先檢查自己是否已經(jīng)完成了GC ROOTS的統(tǒng)計枚舉,如果完成了债沮,那就繼續(xù)往下執(zhí)行代碼炼吴,并將之前進入安全區(qū)域的標識去掉;如果沒有完成GC ROOTS的枚舉疫衩,則該線程中斷硅蹦、等待,直到收到GC ROOTS枚舉已經(jīng)統(tǒng)計完成的信號闷煤,才可以繼續(xù)執(zhí)行下面的代碼童芹。

就醬,本文介紹了GC如何識別出那些需要回收和存活下來的對象鲤拿,并從理論上介紹了GC的回收算法假褪,下篇文章將詳細介紹JVM中的一些具體的垃圾回收器。

(部分圖片源于網(wǎng)絡近顷,如果侵犯到您的權益生音,請聯(lián)系本人刪除。)

著作權歸作者所有窒升。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權缀遍,非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饱须,一起剝皮案震驚了整個濱河市瑟由,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖歹苦,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異督怜,居然都是意外死亡殴瘦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門号杠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚪腋,“玉大人,你說我怎么就攤上這事姨蟋√肟” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵眼溶,是天一觀的道長悠砚。 經(jīng)常有香客問我,道長堂飞,這世上最難降的妖魔是什么灌旧? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮绰筛,結果婚禮上枢泰,老公的妹妹穿的比我還像新娘。我一直安慰自己铝噩,他們只是感情好衡蚂,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骏庸,像睡著了一般毛甲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敞恋,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天丽啡,我揣著相機與錄音,去河邊找鬼硬猫。 笑死补箍,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的啸蜜。 我是一名探鬼主播坑雅,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衬横!你這毒婦竟也來了裹粤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蜂林,失蹤者是張志新(化名)和其女友劉穎遥诉,沒想到半個月后拇泣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡矮锈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年霉翔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苞笨。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡债朵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瀑凝,到底是詐尸還是另有隱情序芦,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布粤咪,位于F島的核電站谚中,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏射窒。R本人自食惡果不足惜藏杖,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脉顿。 院中可真熱鬧蝌麸,春花似錦、人聲如沸艾疟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔽莱。三九已至弟疆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盗冷,已是汗流浹背怠苔。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仪糖,地道東北人柑司。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像锅劝,于是被迫代替她去往敵國和親攒驰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

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