爲什麼初始標記停頓時間與堆大小無關,只與GCROOTS的數量相關

問題如標題所示,複習到Shenandoah收集器時,關於初始標記,書中的描述是:

  • 與G1一樣,首先標記與GC Roots直接關聯的對象,這個階段仍是“Stop The World”的,但停頓時間與堆大小無關,只與GC Roots的數量相關。

上面強調了紅字描述的內容,先了解下GC Roots對象包括那些內容:

  • 所有虛擬機棧的所有棧楨中的本地變量表中引用的對象,譬如各個線程被調用的方法中使用的參數、局部變量、臨時變量等。
  • 在方法區中類靜態屬性引用的對象,譬如Java類的引用類型靜態變量。
  • 在方法區中常量引用的對象。
  • 在本地方法棧中JNI(即通常所說的Native方法)引用的對象。
  • Java虛擬機內部的引用,如基本數據類型對應的Class對象,一些常駐的異常對象(比如NullPointException、OutOfMemoryError)等,還有系統類加載器。
  • 所有同步鎖(synchronized關鍵字)持有的對象。
  • 反映Java虛擬機內部情況的JMXBean、JVMTI中註冊的回調、本地代碼緩存等。

以上是基本描述信息,收集器標記的GC Roots可能會在以上描述基礎上增加GC Roots對象範圍,比如CMS進行老年代收集時,會把年輕代對象作爲GC Roots對象進行處理。不同收集器的GC Roots不盡相同。該文章以Shenandoah爲重點關注對象。與Shenandoah相同的地方可以參考思考,不同的地方需要自己額外去探索。

列舉具體情況比較,相同的應用,cpu不變,系統環境相同、應用請求量也是一樣的,堆內存變大,注G1和Shenandoah收集器年輕代和老年代不是物理隔離,不存在設置年輕代和老年代堆大小問題。

4核cpu     2g堆內存      app1(相同的應用)

4核cpu    8g堆內存      app2(相同的應用)

質疑的問題是:app2、app1觸發垃圾回收時,app2堆中的對象數量肯定比app1多,那麼app2堆中的GCRoots對象也比app1堆中的多。說明堆內存變大,GCRoots數量增多,初始標記時間變長。與文中描述的不一致。

 

大家先思考下以上描述,先看是否認同以上說法,如果不認同思考下原因。

 

開始解答:以上的描述前半句是對的,app2、app1觸發垃圾回收時,app2堆中的對象數量肯定比app1多。然而GCRoots對象卻沒有增加。

首先,GC Roots中的內容變化頻繁的肯定是虛擬機棧,線上每一次請求都會創建一個虛擬機棧,請求中的方法便是棧楨,壓入到棧中,方法執行過程中會在堆中創建對象,棧楨中局部變量引用指向創建的對象。

但是虛擬機棧(包括本地方法棧)是線程私有的,也就是說線程執行結束後,該線程的虛擬機棧就會被釋放,此時堆中留下上次線程創建的對象,由於該線程的虛擬機棧釋放掉了,這些對象沒有引用指向。虛擬機棧都不存在,這些對象當然也不會計入到GC Roots對象集合中。

就是說GCRoots對象只與當前正在執行的線程數有關(即應用的請求數量),這就爲什麼上面強調線上請求量也是一樣的,

就是app1和app2在垃圾回收時,都是當前各有1000個線程在執行,假設1000個是相同的線程,那麼這兩撥線程的虛擬機棧關聯的GCRoots都是一樣的。

 

以上就是全部內容,如有什麼疑問,請及時留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章