為什么我的應(yīng)用會卡頓?垃圾回收中的STW難題與破解之道
垃圾回收算法:清除、壓縮、復(fù)制
可達(dá)性分析提供了一種有效的方式,來標(biāo)記哪些對象死亡,哪些對象還存活。然而,確定哪些對象死亡可以被回收,只是垃圾回收的第一步, 這個過程通常被稱為標(biāo)記(Mark)。接下來,需要一種方法來回收這些死亡對象占用的內(nèi)存,以便這些內(nèi)存可以被重新使用。這就是垃圾回收算法的任務(wù)。
垃圾回收算法描述了如何有效地回收垃圾對象的內(nèi)存,同時盡量減少對程序執(zhí)行的影響。
清除
清除(Sweep)算法的主要操作是將不再活躍的對象的內(nèi)存標(biāo)記為可用,并將這些內(nèi)存信息記錄在一個叫做空閑列表(Free List)的數(shù)據(jù)結(jié)構(gòu)中。當(dāng)程序需要實例化新的對象時,內(nèi)存管理模塊會從空閑列表中找到可用的內(nèi)存空間,分配給新對象。
清除算法的主要缺點是可能導(dǎo)致內(nèi)存碎片化。因為在堆內(nèi)存中,對象的存儲必須是連續(xù)的,可能會出現(xiàn)總的空閑內(nèi)存充足,但無法找到足夠大的連續(xù)內(nèi)存空間來存儲新的對象的情況。
另一個缺點是清除策略的內(nèi)存分配效率較低。如果內(nèi)存是連續(xù)的空間,可以通過簡單的指針運算,比如指針加法(Pointer Bumping),快速分配內(nèi)存。但對于清除算法中的空閑列表,需要逐一檢查列表中的每一項,找到足夠大的空閑內(nèi)存來存儲新的對象,這個過程相對耗時

壓縮
壓縮(Compact)算法的主要操作是將所有存活的對象移動至內(nèi)存的一端,使這些對象在內(nèi)存中連續(xù)排列,并更新所有指向這些對象的引用。這樣,所有未被標(biāo)記的對象都被擠壓到內(nèi)存的另一端,可以一次性回收。
壓縮算法的優(yōu)點是可以避免內(nèi)存碎片,因為所有活動對象在壓縮時都被緊湊排列。此外,這種算法不需要額外的內(nèi)存空間,因為所有操作都在原地完成。
然而,壓縮算法也有缺點。首先,壓縮可能改變對象在內(nèi)存中的位置,可能影響程序性能。其次,如果活動對象占據(jù)了大部分內(nèi)存,壓縮過程可能會非常耗時。

復(fù)制
復(fù)制(Copy)算法的主要操作是將所有活動的對象復(fù)制到內(nèi)存的另一部分(通常稱為to-space),并更新所有指向這些對象的引用。復(fù)制后,原來的內(nèi)存區(qū)域(即from-space)中的所有對象都被視為垃圾,可以一次性回收。
復(fù)制算法的優(yōu)勢在于避免內(nèi)存碎片,因為所有活動對象在復(fù)制時都被緊湊排列。此外,由于只處理活動對象,所以當(dāng)大部分內(nèi)存被垃圾對象占據(jù)時,此算法效率高。
然而,復(fù)制算法也有缺點。首先,它需要額外內(nèi)存空間存放復(fù)制的對象。其次,復(fù)制過程可能改變對象在內(nèi)存中的位置,可能影響程序性能。

并發(fā)標(biāo)記:與時間賽跑的追蹤游戲
標(biāo)記階段是所有追蹤式垃圾回收算法的共同特征,這個階段會隨著堆變大而等比例增加停頓時間,其影響就會波及幾乎所有的垃圾回收過程,同理可知,如果能夠削減這部分停頓時間的話,那收益也將會是系統(tǒng)性的。
為了解決原始標(biāo)記階段帶來的長時間停頓,多數(shù)現(xiàn)代的追蹤式垃圾回收算法都會實現(xiàn)三色標(biāo)記(Tri-color Marking)算法的變種以縮短停頓的時間。三色標(biāo)記算法將程序中的對象分成白色、黑色和灰色三類。
1)白色對象:不活動對象,沒有被其他對象引用,或者從根節(jié)點開始無法到達(dá)的對象;
2)灰色對象:活動對象,被其他對象引用,或者從根節(jié)點開始可以到達(dá)的對象。但是,這些對象引用的對象還沒有被檢查;
3)黑色對象:活動對象,從根節(jié)點開始可以到達(dá)的對象,而且這些對象引用的對象都已經(jīng)被檢查過了。

在垃圾回收過程中,首先將所有對象標(biāo)記為白色,然后從根節(jié)點開始,將可達(dá)的對象標(biāo)記為灰色,然后逐步將灰色對象標(biāo)記為黑色,并將它們引用的對象標(biāo)記為灰色。這個過程一直持續(xù)到所有活動對象都被標(biāo)記為黑色,所有不活動對象都被標(biāo)記為白色。

三色標(biāo)記算法在并發(fā)環(huán)境下可能會出現(xiàn)問題,這個問題被稱為“并發(fā)標(biāo)記的漏標(biāo)問題”。如下圖所示的三色標(biāo)記過程中,用戶程序重新建立了從A對象到D對象的引用,但是因為程序中已經(jīng)不存在灰色對象了,導(dǎo)致D對象本應(yīng)被標(biāo)記為灰色,而被錯誤地標(biāo)記為白色,從而在垃圾回收時被錯誤地回收。

為了解決這個問題,可以使用寫屏障(Write Barrier)技術(shù)。寫屏障像是一個鉤子方法,當(dāng)一個對象的引用被修改時,會觸發(fā)執(zhí)行一段指令代碼,將這個對象重新標(biāo)記為灰色,以確保不會錯過任何需要被標(biāo)記的對象。這樣,就可以在并發(fā)環(huán)境下正確地進行垃圾回收。

增量更新(Incremental Update)和快照在寫時復(fù)制(Snapshot At The Beginning, SATB)都是垃圾回收中的寫屏障技術(shù),但是,它們在處理方式上有所不同。
1)增量更新:寫屏障被觸發(fā)時,如果一個黑色對象引用了一個白色對象,那么這個白色對象會被立即標(biāo)記為灰色。
2)快照在寫時復(fù)制:寫屏障被觸發(fā)時,會記錄下被修改的引用,而不是立即修改對象的顏色。然后在并發(fā)標(biāo)記結(jié)束時,根據(jù)這些記錄,重新標(biāo)記那些被錯誤地標(biāo)記為非活動對象。
未完待續(xù)
很高興與你相遇!如果你喜歡本文內(nèi)容,記得關(guān)注哦
本文來自博客園,作者:poemyang,轉(zhuǎn)載請注明原文鏈接:http://www.rzrgm.cn/poemyang/p/19176615
浙公網(wǎng)安備 33010602011771號