GC之OopMap、Safe Point和Safe Region

1.OopMap

在正式的GC以前,要進行可達性分析來標記出未來可能要宣告死亡的對象。若是每次GC的時候都要遍歷全部的引用,這樣的工做量是很是大的。由於在可達性分析的時候要保證期間不發生引用關係的變化,全部執行線程要停頓等待,稱爲「Stop The World」,程序中的線程須要中止來配合可達性分析。java

因此,每次直接遍歷整個引用鏈確定是不現實的。 爲了應對這種尷尬的問題,最先有保守式GC和後來的準確式GC。這裏準確式GC就會提到一個OopMap,用來保存類型的映射表。安全

1.1 保守式GC

在進行GC的時候,會從一些已知的位置(GC Roots)開始掃描內存,掃描到一個數字就判斷他是否是多是指向GC堆中的一個指針【這裏會涉及上下邊界檢查(GC堆的上下界是已知的)、對齊檢查(一般分配空間的時候會有對齊要求,假如說是4字節對齊,那麼不能被4整除的數字就確定不是指針)等】,而後一直遞歸掃描下去,最後完成可達性分析。這種模糊的判斷方法由於沒法準確判斷一個位置上是不是真的指向GC堆中的指針,因此被命名爲保守式GCoop

優勢:這種可達性分析的方式由於不須要準確的判斷出一個指針,因此效率快。線程

缺點:設計

  • 由於是模糊的檢查,因此對於一些已經死掉的對象,極可能會被誤認爲仍有地方引用它們,GC也就天然不會回收它們,從而引發了無用的內存佔用,形成資源浪費。
  • 因爲不知道疑似指針是否真的是指針,因此它們的值都不能改寫,移動對象就意味着要修正指針。換言之,對象就不可移動了。有一種辦法能夠在使用保守式GC的同時支持對象的移動,那就是增長一個間接層,不直接經過指針來實現引用,而是添加一層「句柄」(handle)在中間,全部引用先指到一個句柄表裏,再從句柄表找到實際對象。這樣,要移動對象的話,只要修改句柄表裏的內容便可。可是這樣的話引用的訪問速度就下降了。Sun JDK的Classic VM用過這種全handle的設計,但效果實在算不上好。
1.2 準確式GC

與保守式GC相對的就是準確式GC。何爲準確式GC?就是咱們準確的知道,某個位置上面是不是指針。對於java來講,就是知道對於某個位置上的數據是什麼類型的,這樣就能夠判斷出全部的位置上的數據是否是指向GC堆的引用,包括棧和寄存器裏的數據。指針

實現這種要求的方法有好幾種,可是在java中實現的方式是:從外部記錄下類型信息,存成映射表,在HotSpot中把這種映射表稱之爲OopMap,不一樣的虛擬機名稱可能不同。對象

實現這種功能,須要虛擬機的解釋器和JIT編譯器支持,由它們來生成OopMap。生成這樣的映射表通常有兩種方式:遞歸

  • 每次都遍歷原始的映射表,循環的一個個偏移量掃描過去;這種用法也叫「解釋式」;
  • 爲每一個映射表生成一塊定製的掃描代碼(想像掃描映射表的循環被展開的樣子),之後每次要用映射表就直接執行生成的掃描代碼;這種用法也叫「編譯式」。

總而言之,GC停頓的時候,虛擬機能夠經過OopMap這樣的一個映射表知道,在對象內的什麼偏移量上是什麼類型的數據,並且特定的位置記錄着棧和寄存器中哪些位置是引用。內存

2.Safe Point

有了OopMap,HotSpot能夠快速準確完成GC Roots枚舉。可是另外一個問題來了,咱們要在什麼地方建立OopMap?程序運行期間,引用的變化在不斷髮生,若每條指令都生成OopMap,那佔用空間就太大了,因此有了安全點(Safe Point)只在安全點進行GC停頓,只要保證引用變化的記錄完成於GC停頓以前就能夠資源

安全點選定太少,GC等待時間就太長,選的太多,GC就過於頻繁。選定原則是「具備讓程序長時間執行的特徵」,也就是在這個時刻現有的指令是能夠複用的。通常選在方法調用、循環跳轉、拋出異常的位置。

如何使線程在Safe Point停頓,方案有兩種:搶先式中斷(棄用)、主動式中斷

  • 搶先式中斷:GC發生時,中斷全部線程,若是發現有線程不在安全點上,就恢復線程讓它運行到安全點上。如今幾乎不用這種方案。
  • 主動式中斷:設置一個標誌,和安全點重合。各個線程主動輪詢這個標誌,發現中斷標誌位就將線程中斷掛起。HotSpot使用主動式中斷,避免了搶佔式中斷的「中斷-啓動-中斷」過程。

3.Safe Region

安全點能夠保證大部分線程停頓,可是當GC請求中斷時線程並無獲取CPU執行權,線程沒法響應JVM「跑到」安全點,可是JVM的GC不會等待線程得到CPU執行權再對它進行可達性分析或者回收。也就是說該線程如今在非安全點中止,而且GC對其進行了操做,這樣的操做是不知足一致性的,當線程甦醒以後就會發生下面的狀況,即線程在繼續執行,GC也在操做該線程(這個過程對象的引用關係有可能會改變)。

安全區域(Safe Region)就能夠很好地解決這樣的問題:安全區域是指在一段代碼片斷中,引用關係不會發生變化,在該區域的任何地方發生GC中斷請求都是安全的。當線程執行到安全區域時,首先標識本身已經進入了安全區域。在線程離開安全區域時,會檢查系統是否正在執行GC,若是是,就等到GC完成後再離開安全區域。

相關文章
相關標籤/搜索