問題如標題所示,複習到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都是一樣的。
以上就是全部內容,如有什麼疑問,請及時留言。