在正式的GC以前,要進行可達性分析來標記出未來可能要宣告死亡的對象。若是每次GC的時候都要遍歷全部的引用,這樣的工做量是很是大的。由於在可達性分析的時候要保證期間不發生引用關係的變化,全部執行線程要停頓等待,稱爲「Stop The World」,程序中的線程須要中止來配合可達性分析。java
因此,每次直接遍歷整個引用鏈確定是不現實的。 爲了應對這種尷尬的問題,最先有保守式GC和後來的準確式GC。這裏準確式GC就會提到一個OopMap,用來保存類型的映射表。安全
在進行GC的時候,會從一些已知的位置(GC Roots)開始掃描內存,掃描到一個數字就判斷他是否是多是指向GC堆中的一個指針【這裏會涉及上下邊界檢查(GC堆的上下界是已知的)、對齊檢查(一般分配空間的時候會有對齊要求,假如說是4字節對齊,那麼不能被4整除的數字就確定不是指針)等】,而後一直遞歸掃描下去,最後完成可達性分析。這種模糊的判斷方法由於沒法準確判斷一個位置上是不是真的指向GC堆中的指針,因此被命名爲保守式GC。oop
優勢:這種可達性分析的方式由於不須要準確的判斷出一個指針,因此效率快。線程
缺點:設計
與保守式GC相對的就是準確式GC。何爲準確式GC?就是咱們準確的知道,某個位置上面是不是指針。對於java來講,就是知道對於某個位置上的數據是什麼類型的,這樣就能夠判斷出全部的位置上的數據是否是指向GC堆的引用,包括棧和寄存器裏的數據。指針
實現這種要求的方法有好幾種,可是在java中實現的方式是:從外部記錄下類型信息,存成映射表,在HotSpot中把這種映射表稱之爲OopMap,不一樣的虛擬機名稱可能不同。對象
實現這種功能,須要虛擬機的解釋器和JIT編譯器支持,由它們來生成OopMap。生成這樣的映射表通常有兩種方式:遞歸
總而言之,GC停頓的時候,虛擬機能夠經過OopMap這樣的一個映射表知道,在對象內的什麼偏移量上是什麼類型的數據,並且特定的位置記錄着棧和寄存器中哪些位置是引用。內存
有了OopMap,HotSpot能夠快速準確完成GC Roots枚舉。可是另外一個問題來了,咱們要在什麼地方建立OopMap?程序運行期間,引用的變化在不斷髮生,若每條指令都生成OopMap,那佔用空間就太大了,因此有了安全點(Safe Point)。只在安全點進行GC停頓,只要保證引用變化的記錄完成於GC停頓以前就能夠。資源
安全點選定太少,GC等待時間就太長,選的太多,GC就過於頻繁。選定原則是「具備讓程序長時間執行的特徵」,也就是在這個時刻現有的指令是能夠複用的。通常選在方法調用、循環跳轉、拋出異常的位置。
如何使線程在Safe Point停頓,方案有兩種:搶先式中斷(棄用)、主動式中斷。
安全點能夠保證大部分線程停頓,可是當GC請求中斷時線程並無獲取CPU執行權,線程沒法響應JVM「跑到」安全點,可是JVM的GC不會等待線程得到CPU執行權再對它進行可達性分析或者回收。也就是說該線程如今在非安全點中止,而且GC對其進行了操做,這樣的操做是不知足一致性的,當線程甦醒以後就會發生下面的狀況,即線程在繼續執行,GC也在操做該線程(這個過程對象的引用關係有可能會改變)。
安全區域(Safe Region)就能夠很好地解決這樣的問題:安全區域是指在一段代碼片斷中,引用關係不會發生變化,在該區域的任何地方發生GC中斷請求都是安全的。當線程執行到安全區域時,首先標識本身已經進入了安全區域。在線程離開安全區域時,會檢查系統是否正在執行GC,若是是,就等到GC完成後再離開安全區域。