Java將堆內存分爲3大部分:新生代、老年代和永久代,其中新生代又進一步劃分爲Eden、S0、S1(Survivor)三個區。結構以下圖所示:算法
Java將堆內存分爲3大部分:新生代、老年代和永久代,其中新生代又進一步劃分爲Eden、S0、S1(Survivor)三個區。結構以下圖所示:多線程
程序中new出來的對象會在新生代裏的Eden區裏面分配空間,若是存活時間足夠長將會進入Survivor區,進而若是存活時間再長,還會被提高分配到老年代裏面。持久代裏面存放的是Class類元數據、方法描述等。併發
1.S0和S1是兩個大小相等的區域,分配內存空間只會在其中某一個進行,另一個空間是用來輔助進行新生代進行垃圾回收的,由於新生代的垃圾回收策略基於複製算法,其思想是將Eden區及兩個Survivor中的某個區,如S0區裏面須要存活的對象複製到另一個空的Survivor區,如S1區,而後就能夠回收Eden和S0區域裏面的死亡對象。下一次回收就對調S0和S1兩個區的角色,S1用來存放存活對象而S0用來輔助回收垃圾,如此循環利用。 .net
2.有些文章並不將永久代歸入Java堆內存。其實永久代就是咱們所說的方法區,而方法區常常被稱爲Non-Heap(非堆)。僅僅在HotSpot虛擬機的實現中才將GC分代收集擴展至方法區,或者說使用永久代來實現方法區,對於其餘的虛擬機是不存在永久代這個概念的。 線程
3.並不是全部的對象建立都會在Eden區中分配內存空間。對於Serial和ParNew垃圾收集器,經過指定-XX:PretenureSizeThreshold={size}來設置超過這個閾值大小的對象直接進入老年代。對象
垃圾回收主要針對Java堆內存中的新生代和老年代,也正由於新生代和老年代結構上的不一樣,因此產生了分代回收算法,即新生代的垃圾回收和老年代的垃圾回收採用的是不一樣的回收算法。針對新生代,主要採用複製算法,而針對老年代,一般採用標記-清除算法或者標記-整理算法來進行回收。blog
複製算法的思想是將內存分紅大小相等的兩塊區域,每次使用其中的一塊。當這一塊的內存用完了,就將還存活的對象複製到另外一塊區域上,而後對該塊進行內存回收。內存
這個算法實現簡單,而且也相對高效,可是代價就是須要將犧牲一半的內存空間用於進行復制。有研究代表,新生代中的對象98%存活期很短,因此並不須要以1:1的比例來劃分整個新生代,一般的作法是將新生代內存空間劃分紅一塊較大的Eden區和兩塊較小的Survivor區,兩塊Survivor區域的大小保持一致。每次使用Eden和其中一塊Survivor區,當回收的時候,將還存活的對象複製到另一塊Survivor空間上,最後清除Eden區和一開始使用的Survivor區。假如用於複製的Survivor區放不下存活的對象,那麼會將對象存到老年代。get
HotSpot虛擬機默認Eden和Survivor的大小比例是8:1:1,也就是說新生代中犧牲掉10%的空間而不是一半的空間。虛擬機
標記-清除(Mark-Sweep)算法分爲兩個階段:
標記
清除
在標記階段將標記出須要回收的對象空間,而後在下一個階段清除階段裏面,將這些標記出來的對象空間回收掉。這種算法有兩個主要問題:一個是標記和清除的效率不高,另外一個問題是在清理以後會產生大量不連續的內存碎片,這樣會致使在分配大對象時候沒法找到足夠的連續內存而觸發另外一次垃圾收集動做。
標記-整理(Mark-Compact)算法有效預防了標記-清除算法中可能產生過多內存碎片的問題。在標記須要回收的對象之後,它會將全部存活的對象空間挪到一塊兒,而後再執行清理。
標記-整理一般會在標記-清除算法裏面做爲備選方案,爲了防止標記-清除後產生大量內存碎片而沒法爲大對象分配足夠內存的狀況
由於新生代和老年代採用回收算法的不一樣,垃圾收集器相應地也分爲新生代收集器和老年代收集器。其中新生代收集器主要有Serial收集器、ParNew收集器和Parallel Scavenge收集器。老年代收集器主要有Serial Old收集器、Parallel Old收集器和CMS收集器。固然還包括了一款全新的、新生代老年代通用的G1收集器。
上圖展現了7種做用於不一樣分代的收集器,若是兩個收集器之間存在連線,就說明它們能夠搭配使用。
Serial收集器做用於新生代,是一個單線程收集器,基於複製算法實現。在進行垃圾回收的時候僅使用單條線程而且在回收的過程當中會掛起全部的用戶線程(Stop The World)。Serial收集器是JVM client模式下默認的新生代收集器。
特別注意,Stop-The-World會掛起應用線程,形成應用的停頓。
ParNew收集器做用於新生代,是一個多線程收集器,基於複製算法實現。相對於Serial收集器而言,在垃圾回收的時候會同時使用多條線程進行回收,可是它跟Serial收集器同樣,在回收過程當中也是會掛起全部的用戶線程,從而形成應用的停頓。
Parallel Scavenge收集器一樣做用於新生代,而且也是採用多線程和複製算法來進行垃圾回收。Parallel Scavenge收集器關注的是吞吐量,即便得應用可以充分使用CPU。它與ParNew收集器同樣,在回收過程會掛起全部的用戶線程,形成應用停頓。
Serial Old收集器做用於老年代,採用單線程和標記-整理算法來實現垃圾回收。在回收垃圾的時候一樣會掛起全部用戶線程,形成應用的停頓。通常來講,老年代的容量都比新生代要大,因此當發生老年代的垃圾回收時,STW經歷的時間會比新生代所用的時間長得多。該收集器是JVM client模式下默認的老年代收集器。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,採用多線程和標記-整理算法來實現老年代的垃圾回收。這個收集器主要是爲了配合Parallel Scavenge收集器的使用,即當新生代選擇了Parallel Scavenge收集器的狀況下,老年代能夠選擇Parallel Old收集器。
CMS(Concurrent Mark Sweep)收集器是一款真正實現了併發收集的老年代收集器。CMS收集器以獲取最短回收停頓時間爲目標,採用多線程併發以及標記-清除算法來實現垃圾回收。CMS只在初始化標記和從新標記階段須要掛起用戶線程,形成必定的應用停頓(STW),而其餘階段收集線程均可以與用戶線程併發交替進行,沒必要掛起用戶線程,因此並不會形成應用的停頓。CMS收集器能夠最大程度地減小因垃圾回收而形成應用停頓的時間。
G1收集器的優點:
(1)並行與併發
(2)分代收集
(3)空間整理 (標記整理算法,複製算法)
(4)可預測的停頓