JVM性能調優之三垃圾回收機制

     我們大家都知道,java的內存回收不需要程序員過多關注,是由JVM自行回收的。但是完全依靠JVM的GC,在一些場景下可能效果不是很好,需要人爲調優。

     面對不同的業務場景,垃圾回收的調優策略也不一樣。例如,在對內存要求苛刻的情況下,需要提高對象的回收效率;在 CPU 使用率高的情況下,需要降低高併發時垃圾回收的頻率。可以說,垃圾回收的調優是一項必備技能。

【垃圾回收機制GC】

      從JVM性能調優之一JVM內存結構可以知道,JVM內存包含五大塊。而垃圾回收則發生在堆和方法區。一般情況下一個對象不再被引用,就代表該對象可以被回收了。目前有2種算法可以判斷對象是否可以被回收。

    引用計數算法:這種算法是通過一個對象的引用計數器來判斷該對象是否被引用了。每一個對象被引用時計數器就加1,而當引用失效時,計數器就減1.。當計數器爲0時,表示該對象不再被引用了。表示可以被GC回收了。

   可達性費心算法:GC  Roots  是該算法的基礎,GC Roots 是所有對象的根對象,在 JVM 加載時,會創建一些普通對象引用正常對象。這些對象作爲正常對象的起始點,在垃圾回收時,會從這些 GC Roots 開始向下搜索,當一個對象到 GC Roots 沒有任何引用鏈相連時,就證明此對象是不可用的。目前 HotSpot 虛擬機採用的就是這種算法。

     需要說明一點,一個對象雖然沒有再被引用了,JVM也不一定會馬上回收改對象,GC在JVM中是自動執行的,java程序無法強制執行。我們唯一能做的是是通過調用System.gcf方法來“建議”執行GC,但是什麼時候執行仍不能知道。

【GC算法】

    

 

【衡量GC的性能指標】

     吞吐量: 這裏的吞吐量是指應用程序所花費的時間和系統總運行時間的比值。我們可以按照這個公式來計算 GC 的吞吐量:系統總運行時間 = 應用程序耗時 +GC 耗時。如果系統運行了 100 分鐘,GC 耗時 1 分鐘,則系統吞吐量爲 99%。GC 的吞吐量一般不能低於 95%。
    停頓時間:指垃圾收集器正在運行時,應用程序的暫停時間。對於串行回收器而言,停頓時間可能會比較長;而使用併發回收器,由於垃圾收集器和應用程序交替運行,程序的停頓時間就會變短,但其效率很可能不如獨佔垃圾收集器,系統的吞吐量也很可能會降低。
    垃圾回收頻率:多久發生一次指垃圾回收呢?通常垃圾回收的頻率越低越好,增大堆內存空間可以有效降低垃圾回收發生的頻率,但同時也意味着堆積的回收對象越多,最終也會增加回收時的停頓時間。所以我們只要適當地增大堆內存空間,保證正常的垃圾回收頻率即可。

 

【GC調優策略】

      1. 降低 Minor GC 頻率
       通常情況下,由於新生代空間較小,Eden 區很快被填滿,就會導致頻繁 Minor GC,因此我們可以通過增大新生代空間來降低 Minor GC 的頻率。

     2. 降低 Full GC 的頻率
        通常情況下,由於堆內存空間不足或老年代對象太多,會觸發 Full GC,頻繁的 Full  GC 會帶來上下文切換,增加系統的性能開銷。我們可以使用哪些方法來降低 Full GC 的頻率呢?
       減少創建大對象:在平常的業務場景中,我們習慣一次性從數據庫中查詢出一個大對象用於 web 端顯示。例如,我之前碰到過一個一次性查詢出 60 個字段的業務操作,這種大對象如果超過年輕代最大對象閾值,會被直接創建在老年代;即使被創建在了年輕代,由於年輕代的內存空間有限,通過 Minor GC 之後也會進入到老年代。這種大對象很容易產生較多的 Full GC。
我們可以將這種大對象拆解出來,首次只查詢一些比較重要的字段,如果還需要其它字段輔助查看,再通過第二次查詢顯示剩餘的字段。
       增大堆內存空間:在堆內存不足的情況下,增大堆內存空間,且設置初始化堆內存爲最大堆內存,也可以降低 Full GC 的頻率。
3、選擇合適的 GC 回收器
         假設我們有這樣一個需求,要求每次操作的響應時間必須在 500ms 以內。這個時候我們一般會選擇響應速度較快的 GC 回收器,CMS(Concurrent Mark Sweep)回收器和 G1 回收器都是不錯的選擇。
       而當我們的需求對系統吞吐量有要求時,就可以選擇 Parallel Scavenge 回收器來提高系統的吞吐量。

發佈了62 篇原創文章 · 獲贊 28 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章