JVM(七)——垃圾收集器的補充說明

前言

之前總結過各種垃圾收集器,但是沒有針對如何選擇垃圾收集器,並沒有做一個合適的介紹,這篇博客會在之前博客的基礎上,先總結所有垃圾收集器,然後再總結一下垃圾收集器的選擇策略,之前博客的鏈接地址——JVM——垃圾收集器

GC Root

之前已經總結過一些垃圾回收算法,但是哪些對象能做爲GC Root對象之前只是在博客中簡單總結了一筆,但是沒探討原因,前兩篇博客的總結,其實是順便將JVM的各個內存模型和佈局都梳理了一遍,重要的是這個圖。
在這裏插入圖片描述
我們這裏探討的GC Root其實是通俗點解釋就是有資格以上帝視角連接堆中對象的實例。所以能稱爲上帝視角的實例對象,首先滿足的一個條件就是——不能在堆中(如果在堆中,還有啥上帝視角可言?)所以從上圖中來看,首先最有可能成爲GC Root對象的就是虛擬機棧中的本地變量。同樣的本地方法棧中的變量也可能成爲GC Root
我們之前總結的時候,還有一張圖
在這裏插入圖片描述

這個方法區中會分配靜態變量,靜態變量會分配在方法區,然後指向堆中指定的對象,並不會隨着堆被創建。靜態變量完全可以有上帝視角,因此第二種可能成爲GC Root的就是靜態變量

此外還有類加載器和Thread線程類,因爲這兩者生命週期較長,同時也在堆之外,完全具備上帝視角。

總結一下,可能成爲GC Root的對象爲:

1、虛擬機棧中的引用對象

2、方法區中的靜態屬性引用的對象和常量引用對象。

3、本地方法棧中的JNI引用對象

4、類加載器和活着的Thread對象

這些不就是《深入理解Java虛擬機》一書中所提到過的麼?不用刻意去記憶,只需要從所謂的上帝視角切入,就能理解爲啥這些對象能作爲GC Root的對象。

垃圾回收算法

這個之前總結過,博客地址:對象與垃圾回收策略
總的來說有標記-清除(Mark-Sweep)、複製、標記整理(Mark-Compact)三種大類,但是針對新生代和老年代對象生命週期不同的特點,JVM在設計的時候就採用了分代的思想,即不懂的分代採用的算法不同,但也沒逃出上述三種的思想描述。總的來講
Young區:採用複製算法(因爲新生代的對象生命週期一般比較短,爲了一定程度上減少內存碎片,Young區採用複製的效率比較高,因爲存活的對象比較少,複製也沒多大壓力)
Old區:採用標記清理算法(因爲老年代的對象生命週期比較長,沒必要複製對內存空間進行清理,只需做個標記即可)

垃圾收集器

之前針對垃圾收集器也做過總結,如下圖所示。
img
這裏在之前博客的基礎上要補充的是,從上圖中可以看出Serial,ParNew和ParallelGC是新生代的垃圾收集器,之前也總結了,新生代垃圾收集器採用複製算法似乎更合理,因此上述三者其實採用的就是複製算法來進行垃圾回收。
這裏需要補充說明一下ParNew和ParallelGC兩者的區別
ParNew其實就是Serial的多線程版本,ParallelGC其實也是一個多線程版本的垃圾收集器但是ParallelGC的關注點不一樣,**ParallelGC更加關注的是吞吐量。**吞吐量=用戶線程執行耗時/(用戶線程執行耗時+垃圾收集線程執行耗時)。例如:用戶線程執行99ms,垃圾收集時間耗費1ms,則系統吞吐量是99%。
之前也說道,Old區採用標記整理的算法比較合適,因此上圖中在老年代的三個垃圾收集器是針對標記整理算法的實現。

併發與並行:
在JVM裏面,可以看到併發與並行的一個區別,併發:用戶和垃圾線程同步進行。並行:有多個線程同時進行垃圾收集的操作

CMS

這裏依舊補充說明一下CMS垃圾收集器,這個收集器的重點關注在停頓時間上面,(其實衡量一個垃圾收集器需要從兩個維度來衡量,一個是吞吐量,一個就是停頓響應時間)。
官網中關於CMS的介紹——官網關於CMS的導讀
The Concurrent Mark Sweep (CMS) collector is designed for applications that prefer shorter garbage collection pauses and that can afford to share processor resources with the garbage collector while the application is running.
官網中有一段這樣的描述,可以很明確的知道,CMS是爲了追求更短的響應時間。

G1

G1垃圾收集器,可以由用戶設定響應時間,適用於新生代也適用於老年代。官網有如下描 ——Oracle G1垃圾收集器,其中有如下一段話:It attempts to meet garbage collection (GC) pause time goals with high probability while achieving high throughput. G1垃圾收集器是爲了提高吞吐量並縮短響應時間而產生的,其整個流程如下所示。
在這裏插入圖片描述
這裏需要說一下其中的篩選回收階段,篩選回收階段其實是G1選擇性回收的階段,會根據用戶設置的響應時間清理垃圾對象最多的幾個區域,如果用戶設置的響應時間過短,則G1清理的區域會相對較少,如果用戶設置的響應時間較長,則G1清理的區域會相對較多,清理的順序按照垃圾對象的比例從大到小進行清理,這就是爲什麼成爲G1(Garbage First)的原因。
什麼樣的情況下用G1?
Applications running today with either the CMS or the with parallel compaction would benefit from switching to G1 if the application has one or more of the following traits.
More than 50% of the Java heap is occupied with live data.

The rate of object allocation rate or promotion varies significantly.

The application is experiencing undesired long garbage collection or compaction pauses (longer than 0.5 to 1 second).
官網中的這樣一段話提供了參考:1、堆內存使用超過50%。2、對象分配和存活率較高。3、期望垃圾收集的停頓相應時間較短。可以從這三個角度去參考是否使用G1垃圾回收器。

如何選擇垃圾收集器

這一點需要重要說明,其實我們參考了很多國內的資料,其實關於垃圾收集器在Oracle的官網中早就已經進行了說明——Oracle Selecting a Collector 下面一段描述的非常清晰了

Unless your application has rather strict pause time requirements, first run your application and allow the VM to select a collector. If necessary, adjust the heap size to improve performance. If the performance still does not meet your goals, then use the following guidelines as a starting point for selecting a collector.

If the application has a small data set (up to approximately 100 MB), thenselect the serial collector with the option -XX:+UseSerialGC.

If the application will be run on a single processor and there are no pause time requirements, then let the VM select the collector, or select the serial collector with the option ``-XX:+UseSerialGC.

If (a) peak application performance is the first priority and (b) there are no pause time requirements or pauses of 1 second or longer are acceptable, then let the VM select the collector, or select the parallel collector with ``-XX:+UseParallelGC.

If response time is more important than overall throughput and garbage collection pauses must be kept shorter than approximately 1 second, then select the concurrent collector with ``-XX:+UseConcMarkSweepGC or-XX:+UseG1GC.

除非我們的程序對停頓時間有着很高的要求,否則優先讓虛擬機自己去進行選擇,如果JVM優先選擇的垃圾收集器無法滿足我們的要求,我們就可以通過調整堆的大小來提高性能,如果還不能滿足要求,就按照以下的知道進行垃圾收集器的選擇

1、如果應用程序的數據集合比較小(頂天了100M)則選擇串行化收集器就行了(Serial垃圾收集器)

2、如果應用程序在單核上運行,並且沒有停頓時間的要求,則讓JVM自主選擇或者使用Serial垃圾收集器

3、如果應用程序重點在意的是吞吐量,沒有停頓時間的要求,或者停頓1秒或者更長你們忍受,那麼可以用ParallelGC垃圾收集器

4、如果應用程序更加在意的是響應時間而不是吞吐量,同時要求垃圾收集的實際響應近乎要小於1秒,則最好選擇CMS垃圾收集器和G1收集器。

這些,官網早就描述的非常清楚了。

總結

本篇博客只是對之前的垃圾收集器進行了一個總結,所謂的JVM調優,相當一部分是針對垃圾收集器的停頓時間和吞吐量進行。後續會對一些工具的使用進行總結。

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