我們知道欧瘪,在分析一個(gè)對象是否是存活的時(shí)候有兩種方法眷射,一個(gè)是引用計(jì)數(shù)法,引用計(jì)數(shù)法雖然實(shí)現(xiàn)簡單并且效率較高佛掖,但是很難解決循環(huán)引用妖碉。所以目前主流的虛擬機(jī)都是使用的是:可達(dá)性分析法。在可達(dá)性分析法中對象能被回收的條件是沒有引用來引用它苦囱,要做到這點(diǎn)就需要得到所有的GC Roots節(jié)點(diǎn)嗅绸,來從GC Root來遍歷∷和可作為GC Root的主要是全局性引用(例如常量和靜態(tài)變量)鱼鸠,與執(zhí)行上下文(棧幀中的本地變量表)中猛拴。那么如何在這么多的全局變量和棧中的局部變量表中找到棧上的根節(jié)點(diǎn)呢?
在棧中只有一部分?jǐn)?shù)據(jù)是Reference(引用)類型蚀狰,那些非Reference的類型的數(shù)據(jù)對于找到根節(jié)點(diǎn)沒有什么用處愉昆,如果我們對棧全部掃描一遍這是相當(dāng)浪費(fèi)時(shí)間和資源的事情。
那怎么做可以減少回收時(shí)間呢麻蹋?我們很自然的想到可以用空間來換取時(shí)間跛溉,我們可以在某個(gè)位置把棧上代表引用的位置記錄下來,這樣在gc發(fā)生的時(shí)候就不用全部掃描了扮授,在HotSpot中使用的是一種叫做OopMap的數(shù)據(jù)結(jié)構(gòu)來記錄的芳室。對于OopMap可以簡單的理解是存放調(diào)試信息的對象。
在OopMap的協(xié)助下刹勃,我們可以快速的完成GC Roots枚舉堪侯,但我們也不能隨時(shí)隨地都生成OopMap,那樣一方面會(huì)需要更多的空間來存放這些對象荔仁,另一方面效率也會(huì)簡單低下伍宦。所以只會(huì)在特定的位置來記錄一下,主要是正在:
- 循環(huán)的末尾
- 方法臨返回前/調(diào)用方法的call指令后
- 可能拋異常的位置
這些位置稱為安全點(diǎn)乏梁。
關(guān)于安全點(diǎn):
我們在做GC的時(shí)候需要讓jvm停在某個(gè)時(shí)間點(diǎn)上次洼,如果不是這樣我們在分析對象間的引用關(guān)系的時(shí)候,引用關(guān)系還在不斷的變化遇骑。這樣我們的準(zhǔn)確性就無法得到保證卖毁。 安全點(diǎn)就是所有的線程在要GC的時(shí)候停頓的位置。那么如何讓所有的線程都到安全點(diǎn)上在停頓下來呢质蕉?這里有兩種方案可以選擇:
- 搶先式中斷
- 主動(dòng)式中斷
在搶先式中斷中不需要線程主動(dòng)配合势篡,在GC發(fā)生的時(shí)候就讓所有線程都中斷,如果發(fā)現(xiàn)哪個(gè)線程中斷的地方不在安全點(diǎn)上模暗,那么就恢復(fù)線程禁悠,然后讓它跑到安全點(diǎn)上。
而主動(dòng)式中斷是讓GC在需要中斷線程的時(shí)候不直接對線程操作兑宇,設(shè)置一個(gè)標(biāo)志碍侦,讓各個(gè)線程主動(dòng)輪詢這個(gè)標(biāo)志,如果中斷標(biāo)志位真時(shí)就讓自己中斷隶糕。
目前幾乎沒有虛擬機(jī)采用搶先式中斷了瓷产。