首頁>技術>

對JVM的調優,需要大量的場景以及經驗,本篇主要是從一個理論的角度,粗淺地

我把堆區的主要結構以及引數放在下面,這樣可以參照著圖來看:

一、如何設定最大年齡

每發生一次Young GC,就會將Eden區和當前的Survivor區的存活物件一次性地轉入到另外一個Survivor區中,並將之前的Eden區以及Survivor區清空。所以年輕代的存活物件,基本上就是在兩塊Survivor區中換來換去,每換一次,年齡增加1歲。當到達最大年齡時(最大年齡由-XX:MaxTenuringThreshold引數設定,預設15歲),就會被轉移進老年代。

現在有這樣的一個場景,8歲的物件有1000個,過了一段時間後,15歲的物件有900個。可以觀察到,在8歲後,有90%的物件達到了預設的最大年齡,這些物件不停地在兩個Survivor區中換來換去,無疑增加了複製成本。因此,在這種情況下,我們大可以將最大年齡設定為8歲,達到8歲的物件,直接轉移至老年代,避免多次重複複製與浪費新生代空間。

二、Young GC頻繁怎麼辦?

我們使用jstat -gcutil {pid} 1000,即每秒打印出GC的統計資訊,其中YGC代表Young GC 發生的總次數。每秒重新整理一次統計資訊,如果此時發現YGC增加得很頻繁,比如一秒一次Young GC。

Young GC頻繁,代表著新物件的建立速度與新生代大小不匹配,要麼是程式碼中頻繁建立物件,要麼就是新生代的空間太小。排查程式碼是有必要的,但卻非常耗時。那麼這一次,我們主要從調整新生代大小的方案入手。

我們大可以將新生代區增加為1.5倍(為什麼是1.5倍,這只是一個試探的倍數)。如果之前Young GC的每隔1000ms發生一次,那麼理論上現在的Young GC的發生間隔在1500ms左右,頻率有所降低,但是會不會導致每次Young GC的耗時增加為原來的1.5倍呢?

答案是不會的

Young GC主要是對新生代進行清理,首先對Eden區和一塊Survivor區的存活物件進行標記,然後一起復制另外一塊Survivor區中,最後直接清理Eden區和之前的Survivor區。可見,這裡耗時最嚴重的環節是複製操作。

大概98%的物件都是在幾毫秒內死亡,即使將新生代擴充為原來的1.5倍,那麼當下一次Young GC到來時,複製的物件總數遠小於之前的1.5倍,可能只是比之前多一點點,比如是1.15倍。

因此,將新生代擴容至原來的1.5倍,理論上,掃描新生代的時間將會變為原來的1.5倍,標記時間在[1,1.5)倍內,複製時間在[1,1.5)倍內,且這兩個時間遠小於1.5倍。對於虛擬機器來說,複製的消耗成本遠大於掃描與標記操作。因此,擴容新生代後,Young GC不會顯著地按照線性增長。

如果保持整個堆的大小不變,那麼擴容新生代後,勢必會壓縮老年代的空間,Major GC的頻率可能會增加。所以,還是需要找到一個臨界點,在能夠大幅度下降Young GC的頻率時,且只在小幅度內增加Major GC的頻率。

11
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 網路術語解析