safe-point(safepoint 安全點) 和 safe-region(安全區域)

爬蜥發表於2019-01-19

以 GC safe-point引入

GC如何找到不可用的物件

編寫程式碼的時候是可以知道物件不可用的,但對於程式來說,需要一定的方式來知曉,可用方法比如:編譯分析,引用計數,和物件是否可達

可達性分析

一個物件只要能夠通過mutator觸達,那麼它就是“活”著的。如果Mutator棧的一個槽位包含了物件的引用,那麼物件就是直接可觸達。而從直接可達物件可觸達的物件必定也是可達的,因而可達性分析,只需要找到直接可達的引用。

直接可達的引用就是根引用,根引用的集合就是根的集合

mutator的上下文就包含了直接可達的資料,所以要獲取物件根集合就是要找到mutator上下文中的物件引用,而mutator的上下文指的就是它的棧、它的暫存器檔案以及一些執行緒上特定的資料

全域性資料本身也是直接可達的

可達性分析為了確保能正確的決定物件是否存活,GC需要獲取mutator 上下文的一致性快照,然後列舉所有的根物件。

這裡的一致性指的是 快照的抽取就像只在一個時間點發生,來避免丟失一些活著的物件

如何獲取 mutator上下文的一致性快照

一種簡單的方式就是在跟引用的過程中暫停所有的執行緒。當mutator暫停了它的執行時,只有將所有引用資訊儲存在其上下文中,才能列舉根的集合,這意味著,mutator需要能夠告知那些棧的槽位有一用,那些暫存器持有引用。如果GC能夠準確的獲取上述引用資訊,它就稱作精準根集合列舉。

無法獲取就是不精準的,以下只講精準的

如何獲取精準的引用資訊列舉

對於java來說,JIT知曉所有的棧幀資訊和暫存器的內容,當JIT編譯一個方法時,對於每條指令,它都可以去儲存根引用資訊,儲存意味著額外的儲存空間,如果要儲存所有的指令就顯得花銷太大,另外在真實的執行過程中也只有少數指令才會成為暫停點,因此JIT只需要儲存這些指令點的資訊就夠了。而真正有機會成為暫停點的地方就稱作 safe-points,即能夠安全的列舉根集合的暫停點

safe-point 定義

“A point in program where the state of execution is known by the VM”,即程式碼中VM能夠準確知道執行狀態的位置。
safe-point有多個種類

  • GC safepoint,要觸發一次GC,JVM中的所有執行緒都必須達到GC safepoint
  • Deoptimization safepoint,要觸發一次 deoptimization,需要執行deoptimization的執行緒要到達safepoint之後才可以開始deoptimize

Hotspot中兩者實現在一起,概念上沒有直接聯絡,需要資料不一樣

如何保證mutator會在 safe-point暫停

當GC想要觸發一次回收時,它會設定一個標誌,mutator則週期性的去檢查(poll)這個標誌,如果檢查到了,就會立馬暫停,這裡的檢查點(poll points)也是安全點,由JIT負責把poll points放到合適的位置

那些地方適合設定檢查GC事件的標記

polling point插入的主要原則是:

  1. polling point應該足夠多,防止GC等一個mutator的暫停太長,導致其他mutator都走在等GC釋放空間,程式整個等待過長
  2. polling point不能太頻繁導致執行時儲存開銷過大
  3. polling本身也是有開銷的,不能過多

權衡下來只在必須和必要的地方加

  1. 在分配地址的時候強制新增,因為分配空間很有肯能導致回收,所以這裡是一個安全點
  2. 長時間的執行一般意味著迴圈和方法呼叫,所以方法呼叫和迴圈返回最好加上

但是有時候並不是長時間的執行,而是長時間的空閒,比如 sleep、block,執行緒在執行其他的native函式,這些時候JVM無法掌控執行能力,也就無法響應GC事件。

不同的JVM選用不同的位置放置safepoint。

如何解決sleep/block 帶來的問題

引用safe-region。safe-region是指程式碼快中沒有用到會變異的部分,這樣的程式碼塊中,任何一個點都可以安全的列舉根。當進入到safe-region中時,mutator會設定一個準備標記,在離開safe-region區域之前,會檢查GC是否已經完成了回收,如果沒有,那麼就暫停執行,如果有,就可以直接離開safe-region區域,不需要暫停mutator

文章翻譯自 Xiao-Feng Li 部落格

rednaxelafx對safepoint的回答

總結

程式碼的執行過程中,如果需要執行某些操作,比如GC,deoptimize,等等,必須知道當前程式所有執行緒執行到的地方,是否能夠恰好滿足我執行對應操作,而不會對應用程式本身造成損害,這些能夠正確執行操作的地方也就是safepoint/saferegion

相關文章