JVM垃圾回收


1.  概念理解

1.1.  並行(Parallel)與並發(Concurrent)

並行:指多個垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態

並發:指用戶線程與垃圾收集線程同時執行

1.2.  Minor GC 與 Major GC

Minor GC:指發生在新生代的垃圾收集動作,因為Java對象大多都具備朝生夕滅的特性,所以Minor GC非常頻繁,一般回收速度也比較快。

Major GC:指發生在老年代的GC,出現了Major GC,經常會伴隨至少一次的Minor GC。Major GC的速度一般會比Minor GC慢10倍以上。

1.3.  吞吐量

吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 /(運行用戶代碼時間 + 垃圾收集時間)

假設虛擬機總共運行了100分鍾,其中垃圾收集花掉1分鍾,那吞吐量就是99%

2.  對象存活判斷

回收的第一步是標記出哪些需要回收,為此需要有一種方式來判斷對象是否存活,存活的不用回收

判斷的方式是可達性分析,通過遍歷對象的引用樹來找出存活的對象

所謂的引用樹本質上是有根的圖結構,它沿着對象的根句柄向下查找到活着的節點,並標記下來;其余沒有被標記的節點就是死掉的節點,這些對象就是可以被回收的

遍歷從GC Roots開始,那么哪些對象可作為GC Roots對象?

  • 虛擬機棧中應用的對象
  • 方法區里面的靜態對象
  • 方法區常量池的對象
  • 本地方法棧JNI應用的對象

3. 垃圾回收算法

3.1.  標記-清除算法(Mark-Sweep)

標記-清除算法分為兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所占用的空間。

從圖中可以很容易看出標記-清除算法實現起來比較容易,但是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會導致后續過程中需要為大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。

優點:簡單

缺點:容易產生大量碎片

3.2.  復制算法(Copying)

它將可用內存划分為兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象復制到另外一塊上面,然后再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。

Java的新生代串行垃圾回收器中使用了復制算法的思想。新生代分為 eden 空間、survivor from 空間、survivor to 空間 3 個部分。其中survivor from 空間和 survivor to 空間可以視為用於復制的兩塊大小相同、地位相等,且可進行角色互換的空間塊。默認Eden與Survivor的比例是8:1

這種算法雖然實現簡單,運行高效且不容易產生內存碎片,但是卻對內存空間的使用做出了高昂的代價,因為能夠使用的內存縮減到原來的一半。

很顯然,Copying算法的效率跟存活對象的數目多少有很大的關系,如果存活對象很多,那么Copying算法的效率將會大大降低。

優點:簡單,不易產生碎片

缺點:可用內存變少,且如果存活對象較多,則復制的效率會大大降低

3.3.  標記-整理算法(Mark-Compact)

該算法標記階段和Mark-Sweep一樣,但是在完成標記之后,它不是直接清理可回收對象,而是將存活對象都向一端移動,然后清理掉端邊界以外的內存。

復制算法的高效性是建立在存活對象少、垃圾對象多的前提下的。這種情況在年輕代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用復制算法,由於存活的對象較多,復制的成本也將很高。

標記-壓縮算法修復了標記-清除算法的短板——它將所有標記的也就是存活的對象都移動到內存區域的開始位置。這種方法的缺點就是GC暫停的時間會增 長,因為你需要將所有的對象都拷貝到一個新的地方,還得更新它們的引用地址。相對於標記-清除算法,它的優點是不容易產生碎片;相對於復制算法,它的優點是可用內存空間不會減少。

優點:不易產生碎片,充分利用可用內存空間

缺點:用戶等待時間變長

3.4.  分代回收算法(Generational Collection)

分代回收算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據對象存活周期的不同將內存划分為幾塊。根據每塊內存區間的特點,使用不同的回收算法,以提高垃圾回收的效率。

以HotSpot虛擬機為例,它將所有的新建對象都放入稱為年輕代的內存區域,年輕代的特點是對象會很快回收,因此,在年輕代就選擇效率較高的復制算法。當一個對象經過幾 次回收后依然存活,對象就會被放入稱為老生代的內存空間。在老生代中,幾乎所有的對象都是經過幾次垃圾回收后依然得以幸存的。因此,可以認為這些對象在一 段時期內,甚至在應用程序的整個生命周期中,將是常駐內存的。如果依然使用復制算法回收老生代,將需要復制大量對象。再加上老生代的回收性價比也要低於新生代,因此這種做法也是不可取的。根據分代的思想,可以對老年代的回收使用與新生代不同的標記-壓縮算法,以提高垃圾回收效率。

目前大部分垃圾收集器對於新生代都采取Copying算法,因為新生代中每次垃圾回收都要回收大部分對象,也就是說需要復制的操作次數較少,但是實際中並不是按照1:1的比例來划分新生代的空間的,一般來說是將新生代划分為一塊較大的Eden空間和兩塊較小的Survivor空間(一般為8:1:1),每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象復制到另一塊Survivor空間中,然后清理掉Eden和剛才使用過的Survivor空間。

而由於老年代的特點是每次回收都只回收少量對象,一般使用的是Mark-Compact算法。

(PS:年輕代一般使用復制算法,年老代一般使用標記整理算法)

4.  垃圾收集器

新生代收集器:Serial、ParNew、Parallel Scavenge

老年代收集器:Serial Old、Parallel Old、CMS

4.1.  Serial 收集器

串行GC,采用復制算法

4.2.  ParNew 收集器

並行GC,采用復制算法,相當於Serial的多線程版本

4.3.  Parallel Scavenge 收集器

並行GC,采用復制算法。追求高吞吐量,高效利用CPU。吞吐量一般為99%, 吞吐量= 用戶線程時間 / (用戶線程時間+GC線程時間)

這里注意,經過測試JDK 1.8.0_152默認垃圾收集器為:PS MarkSweep 和 PS Scavenge,而並不是G1

4.4.  Serial Old 收集器

串行GC,采用標記整理算法,Serial老年代版本

4.5.  Parallel Old 收集器

並行GC,采用標記整理算法,它是Parallel Scavenge收集器的老年代版本

4.6.  CMS 收集器

並行GC,並發,采用標記清除算法

CMS(Concurrent Mark Sweep) 收集器是一種以獲取最短回收停頓時間為目標的收集器。

CMS整個過程分為4個步驟:

  1. 初始標記(CMS initial mark) -stop the world
  2. 並發標記(CMS concurrent mark)
  3. 重新標記(CMS remark) -stop the world
  4. 並發清除(CMS concurrent sweep)

4.7.  G1 收集器

最先進的收集器,獨立完成分代回收

5.  查看垃圾回收器

5.1.  命令行

java -XX:+PrintCommandLineFlags -version

也就是說,本例中JVM使用的垃圾回收器是 Parallel Scavenge (新生代,並行GC) + Serial Old (老年代,串行GC)

5.2.  代碼

 6.  其它相關

Java8虛擬機內存模型


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM