jvm收集器
如果說垃圾收集算法是垃圾收集的方法論的話,那么垃圾收集器就是對垃圾收集算法的具體實現,在目前到JDK1.7當中,垃圾收器有哪些?JVM中的垃圾收集器Scavenge GC(次收集器) 和 Full GC的區別 (全收集)新生代 GC (Scavenge GC): Scavenge GC 指發生在新生代的GC,因為新生代的Java對象大多是朝生夕死,所以Scavenge GC非常的頻繁,一般回收速度也比較快,當Eden空間不足以為對象分配內存時,會觸發 Scavenge GC.一般情況下,當新對象生成,并且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,并且把尚存活對象移動到Survivor區,然后整理Survivor兩個區,這種方式的GC是對年輕代的Eden進行不會影響到老年代, Eden區的GC會頻繁進行. 因而一般在這里需要使用速度快,效率高的算法,使Eden能盡快的空閑出空間來.老年代GC(Full GC/Major GC): Full GC是指發生老年代的GC,出現了Full GC一般會伴隨著至少一次的Minor GC,一塊內存,分成左右兩塊,左邊是young,右邊是old,young里面又分為Eden, s0, s1,那么我們講的ScavengeGC,和Minor GC 它所對應的點, Full GC不是根據閥值觸發的,而是根據你Scavenge GC無法回收時而觸發的,也就是只要我進行Full GC, 前面肯定進行了多次Minor GC,他不會優先于 Minor GC,而Minor GC 優先于Full GC,就是將軍出馬表示小兵解決不了,這就很好理解次收集當年輕代對空間緊張時會被觸發相對于全收集而言,收集時間更短全收集當老年代或者持久帶空間滿了,會觸發全收集可以使用System.gc()方法來顯示的啟動全收集,但也不是立即去啟動Full GC全收集一般根據堆大小的不同,需要的時間也不盡相同,但一般會比較長,不過,如果全收集的時間超過3到5秒鐘,那就太長了.垃圾回收的方式,我們有兩大類收集器,我們垃圾回收器有兩大類,一大類是對young做回收的,一大類是對old或者permanent做回收的Full GC滯后于Minor GC
分代回收器BEA也有自己的虛擬機,IBM也有自己的虛擬機,young generation 和 tenured generation新生代里又哪些GC, serial GC是新生代收集器,parnew也是一個新生代收集器,parallel scavenge 叫并行收集器,這三個收集器構成了新生代收集器CMS(Couccurent Mark Sweep): 并發標記清除,我們現在有兩個名詞了,一個是并行,一個是并發Serial Old: Parallel Old: 上面已經有6個了,還有一個是新老通吃的,G1,G1是什么意思呢,garbage first是現在最新的一個收集器了JVM中有7個收集器,新生代有3個,老年代3個,新老通吃一個.ParNew是 serial的一個升級
1. Serial收集器是JDK1.3之前JVM唯一選擇的收集器,在次代收集器當中,在1.3版本之前次代收集器只有一個.Serial收集器它是一個串行收集器,從1.5的HotSpot的JVM正式商業化,不同的版本都是用HotSpot,但是到底誰是默認的次收集器,不同的版本都是不一樣的,Serial收集器是HotSpot運行在Client模式下的默認新生代收集器,它的特點是只用一個CPU/一條收集線程去完成GC工作,并且在進行垃圾收集時必須暫停其他所有的工作線程(Stop the world, 后面簡稱STW), 可以使用-XX:+UseSerialGC 打開.-XX:+UseSerialGC 這個命令就是你的JVM是使用什么GC的.
串行收集器最大的特點就是會暫停所有的工作線程,對于這個缺點,SUN公司JVM的工程師,比如看電影看了幾分鐘就暫停幾秒鐘,工程師對于用戶的氣憤也是比較理解,但是他也表示很無奈,合情合理的存在一個矛盾,為什么要把他說的這么詳細呢,因為接下來講到的大部分的收集器,其實都是圍繞著串行收集器做的一個優化,來減少他停頓的時間,甚至不停頓,但是從目前來看,做到真正的不停頓是不可能的,這其實是虛擬機的工程師從1.3到后續的版本一直要找解決方案的問題,JVM有沒有最厲害的收集器,答案是沒有.如果有一個最牛的收集器解決所有的問題,那就沒有必要討論其它的收集器了,那為什么還有這么多的收集器呢,那是因為在不同的情況下都有自己的優缺點我們可以根據不同的情況選擇不同的收集器,選擇收集器也是我們JVM優化的一個點,合理的選擇收集器會讓你的程序在運行當中穩定或者性能更高一點,至少不會出現OOM異常,內存溢出JVM優化的三個點:1. 選擇JVM的版本 是client還是server2. 對于堆區大小的分配,我們還可以通過參數去配置3. 垃圾收集器的選擇不同的收集器有些是不能搭配使用的,新老之間是不能搭配使用的,但有些是可以搭配使用的,比如像CMS,這個是JVM真正意義上的打掃房間可以隨便跑的一個收集器,他是一個比較好的收集器,但是他也有缺點,它和次代收集器不是所有的都可以配合工作串行收集器可以讓工作線程暫停新生代收集器和老年代收集器,新生代收集是采用復制算法,因為新生代里又survivor0,survivor1,如果是serial old是采用標記整理算法并行收集器并行收集器就是對串行收集器的一個升級,讓他變成串行化了,單線程的,但是如果是在單核CPU上,其實parallel的性能不比serial高多少,它在什么情況下可能會更優一點呢,就是在多核CPU下,比如一臺服務器,我們個人PC是單CPU,不需要真正的物理多核,一個單核模擬多核,在一臺服務器上,服務器上可是真正支持多核的,可以插4塊CPU物理節點,CPU越多,對線程支持肯定越好,比如說8塊CPU,加上每塊CPU支持多線程技術可以達到32塊邏輯核,那么在這種情況下,并行ParNew收集器可能會優于串行收集器,在多核情況下,但是它也并沒有解決這個問題,stop the world,只是原來是單線程,現在變成多個線程收集了,只是說縮短了暫停時間點那肯定是縮短了,垃圾回收快了,那暫停時間久短了,原來要停2秒才能收集完,現在可能1秒就收集完了,ParNew收集器其實是前面Serial的多線程版本,除使用多線程進行GC外,包括Serial可用的收集參數,收集算法STW,對象的分配規則,回收策略等都和Serial完全一樣, 是VM啟用CMS收集器-XX:+UserConcMarkSweepGC默認新生代收集器,我們剛剛有一個CMS,也就是說如果老年代啟動了CMS,也就是說CMS和ParNew是可以相互協作使用的,CMS可以和ParNew,Serial一起合作使用,但是不能和 parallel scavenge一起使用,而ParNew作為CMS默認新生代的次收集器由于存在線程切換的開銷,ParNew在單CPU的環境中比不上Serial,且在通過超線程技術的實現的兩個CPU的環境也不能100%保證超越Serial,就是說雖然是超線程的,但是單核的超線程還是一個線程,因為CPU只有一塊,如果你要使用超線程技術,4核或者8核,它的性能也不完全釋放,如果我們要進行配置,可以使用-XX:Parallel GCThread常數表示給當前并行收集器使用多少個回收線程,一般是和你的核數配置一樣的數量
ParNew是串行收集器的一個擴展,在多核CPU下才會顯示它的能力,在單核不見得比Serial串行化好多少其實ParNew是JVM中真正意義上的暫停的收集器,但是也不是說讓工作線程可以真正的運作,而是說讓工作線程工作的同時Parallel Scavenge收集器與ParNew類似, Parallel Scavenge 也是使用復制算法,也是并行多線程收集器,但是Parallel Scanvege與ParNew有一個本質的區別,ParNew是為了安全點的時間長短,不想過長的占用工作線程的時間,但是Paramllel Scavenge 收集器是為了解決吞吐量的問題,當我的收集器在運行的時候,我可以節省我的時間來解決CPU運行的吞吐量,那么咱們的知道什么事吞吐量,其實吞度量就是CPU運行用戶程序與CPU總耗時時間的一個比值,吞吐量有一個公式: 系統吞度量=CPU運行用戶程序的時間/(CPU運行用戶線程的時間+GC所耗時的時間),比如說JVM運行了100分鐘,然后GC回收用了1分鐘,那么這個吞吐量就是99%,也就是說GC耗的時間越低,是不是吞吐量就越高,GC耗的事件越高,吞吐量就越低,所以說Parallel Scavenge降低GC回收時的時間,來提高CPU的吞吐量,這是他的一個出發點,它和ParNew是有區別的,ParNew是不考慮吞度量的,只考慮用戶線程等待的時間,也就是Stop the world時間的長短. 系統吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)
-XX:MaxGCPauseMillis Pause是暫停,暫停的毫秒數-XX:GCTimeRatio 占用垃圾時間的百分比,這個不是設置的越低就越好,因為設置的越低可能收集的次數就越頻繁你的效率可能就會受到一定的影響.
我們已經講了3個收集器了,這3個都是新生代的,我們一共有7大收集器,3個新生代,3個老年代,1個跨代.接下來我們就講老年代的收集器了
老年代收集器1. Serial Old 收集器Serial Old 收集器其實就是 Serial收集器的老年版,換句話說就把它又應用到老年代上,但是不同的是什么呢,你想新生代串行化收集器收集的時候用的Survivor的復制算法,一定要記住復制算法的特點,一個叫Survivor0,一個叫Survivor1,除了young generation以外,tunured能不能不能做復制算法,因為它沒有復制點,所以要明白一個事, Serial old 收集器其實在運用在老年代的時候,也就是要改算法,因為老年代里沒有兩塊區域讓你使用復制算法, Serial old 在老年代里會馬上使用標記整理算法
所以它用的不是標記清除,還記得標記清除和標記整理的區別嗎,用的是標記整理,也就意味著它在清除完對象以后,是不是會對內存空間做整理,不會產生過多的碎片,所以這是他的一個特點, Serial old和年輕代的是一樣的,不過是換了一個算法對老年代做一個回收而已.CMS是可以和Serial搭配使用,CMS可以和新生代的ParNew使用,那么Serial old可以和誰搭配使用呢,其實在老年代的回收器當中,Serial old回收器它兼容性特別好,它可以和年輕代的任何的一個收集器相互使用,相互合作,這是他的一個特點.再來看老年代最后一個收集器Parallel old 收集器Parellel Old 其實就是Parallel Scanvege收集器老年代版本,使用多線程和標記-整理算法,吞吐量優先,它的收集器只能和Parallel Scanvenge搭配使用
CMS 收集器:我們要搞清楚一個概念性的東西,Parallel 和 Concurrent的區別,并行和并發的區別,在JVM當中,所謂的并行是什么意思呢,parallel scavenge 和 parallel old是并行收集器,那什么叫并行呢,其實所謂的并行,就是當前CPU的運行用戶程序的時候,GC的線程可以多線程的方式運行,并發和并行最大的區別一點是包含了用戶線程,并發是我的垃圾線程和用戶的工作線程交替的運行,一起運行,所謂的一起運行也是交替運行,這是并行和并發最大的區別,并行是指GC的線程,我有多條GC的線程同時在運行,和用戶線程沒有關系,用戶線程還是在安全點在停頓著呢,我的這些回收線程一起工作,這個叫并行,如果GC的線程也工作,讓用戶的用戶線程也工作,這個過程叫并發,這個是并行和并發最根本的區別,誰包含了用戶線程,誰沒包含用戶線程,并行是不包含用戶線程的,并發是包含用戶線程的所以又教你一個小竅門,以后只要看到Parallel這個單詞,這個收集器就是并行收集器,是不能和用戶線程一起運行的,但是帶Concurrent的,說明可以和用戶線程一起去工作,但是其實同時一起也是相互交替的,這是他的一個區別,CMS就是這么一個收集器,他的全稱叫做 Concurrent Mark sweep,他最大的區別就是可以和用戶線程一起工作,CMS收集器是一款跨時代的收集器,一款真正意義上的并發收集器,雖然現在已經有了理論意義上表現更好的G1收集器,但現在主流互聯網企業線上選用的仍然是CMS,這個收集器又給我們提供了一個信息,既然他的能力這么強,我們是不是可以把它應用到我們JVM收集指令上,也就是說如果你要換收集器,你可以換CMS收集器,那JVM換他的時候就要考慮到了,他和年輕代的誰一起工作呢,他可以和Serial, Parnew一起工作,但是不能夠和 Parallel Scavenge一起工作,如果老年代要換CMS,那年輕代收集器就只有Serial, ParNew這兩個選擇了,我們項目最終肯定是運行到服務端的,服務端肯定是多CPU的, ParNew和CMS是可以合作的一個點.1. CMS默認啟動的回收線程數=(CPU數據+3)4
我們已經講了6個收集器了,新生代有3個, Serial,ParNew,Parallel Scavenge,這三個有什么區別,Serial在回收的時候會讓工作線程處于等待的狀態,單線程版的, ParNew是Serial的升級版,多線程版本,讓safe point 安全點的時間更短一點,Parallel Scavenge主要是解決吞吐量的問題.然后老年代的有3個,CMS這個回收器它可以把并行化變成并發化,在搭配上可以和Serial搭配,可以和ParNew搭配,Serial Old是Serial的老年版,給老年代回收,還有一個是Parallel Old,并行的老年版.然后我們來講一個G1G1的全稱叫做Garbage First,其實Garbage First在JDK1.5的時候就打算去開發他了,后來在JDK1.7的時候加進入了,它不存在和誰搭配的問題,G1因為是跨代的,所以是通吃的.
?
總結