JVM内存管理------垃圾搜集器精解
轉(zhuǎn)載自? ?JVM內(nèi)存管理------垃圾搜集器精解
引言
? ? ? ? ?在上一章我們已經(jīng)探討過hotspot上垃圾搜集器的實(shí)現(xiàn),一共有六種實(shí)現(xiàn)六種組合。本次LZ與各位一起探討下這六種搜集器各自的威力以及組合的威力如何。
? ? ? ? ?為了方便各位的觀看與對(duì)比,LZ決定采用當(dāng)初寫設(shè)計(jì)模式時(shí)使用的方式,針對(duì)某些搜集器,分幾個(gè)維度去解釋這些搜集器。
?
client模式與server模式
? ? ? ? ?在介紹本章內(nèi)容之前,要說一下JVM的兩種模式,一種是client模式,一種是server模式。我們平時(shí)開發(fā)使用的模式默認(rèn)是client模式,也可以使用命令行參數(shù)-server強(qiáng)制開啟server模式,兩者最大的區(qū)別在于在server模式下JVM做了很多優(yōu)化。
? ? ? ? ?server模式下的JAVA應(yīng)用程序啟動(dòng)較慢,不過由于server模式下JVM所做的優(yōu)化,在程序長時(shí)間運(yùn)行下,運(yùn)行速度將會(huì)越來越快。相反,client模式下的JAVA應(yīng)用程序雖然啟動(dòng)快,但不適合長時(shí)間運(yùn)行,若是運(yùn)行時(shí)間較長的話,則會(huì)在性能上明顯低于server模式。
?
搜集器詳解
? ? ? ? ?以下我們先探討一下單個(gè)垃圾搜集器的相關(guān)內(nèi)容,最后我們?cè)俸?jiǎn)單的談一下組合之后,各個(gè)組合的特點(diǎn)。
? ? ? ? ?
Serial Garbage Collector
? ? ? ? ?算法:采用復(fù)制算法
? ? ? ? ?內(nèi)存區(qū)域:針對(duì)新生代設(shè)計(jì)
? ? ? ? ?執(zhí)行方式:單線程、串行
? ? ? ? ?執(zhí)行過程:當(dāng)新生代內(nèi)存不夠用時(shí),先暫停全部用戶程序,然后開啟一條GC線程使用復(fù)制算法對(duì)垃圾進(jìn)行回收,這一過程中可能會(huì)有一些對(duì)象提升到年老代
? ? ? ? ?特點(diǎn):由于單線程運(yùn)行,且整個(gè)GC階段都要暫停用戶程序,因此會(huì)造成應(yīng)用程序停頓時(shí)間較長,但對(duì)于小規(guī)模的程序來說,卻非常適合。
? ? ? ? ?適用場(chǎng)景:平時(shí)的開發(fā)與調(diào)試程序使用,以及桌面應(yīng)用交互程序。
? ? ? ? ?開啟參數(shù):-XX:+UseSerialGC(client模式默認(rèn)值)?
? ? ? ??
Serial Old?Garbage Collector
? ? ? ? ?這里針對(duì)serial old搜集器不再列舉各個(gè)維度的特點(diǎn),因?yàn)樗cserial搜集器是一樣的,區(qū)別是它是針對(duì)年老代而設(shè)計(jì)的,因此采用標(biāo)記/整理算法。對(duì)于其余的維度特點(diǎn),serial old與serial搜集器一模一樣。
?
ParNew?Garbage Collector
? ? ? ? ?算法:采用復(fù)制算法
? ? ? ? ?內(nèi)存區(qū)域:針對(duì)新生代設(shè)計(jì)
? ? ? ? ?執(zhí)行方式:多線程、并行
? ? ? ? ?執(zhí)行過程:當(dāng)新生代內(nèi)存不夠用時(shí),先暫停全部用戶程序,然后開啟若干條GC線程使用復(fù)制算法并行進(jìn)行垃圾回收,這一過程中可能會(huì)有一些對(duì)象提升到年老代
? ? ? ? ?特點(diǎn):采用多線程并行運(yùn)行,因此會(huì)對(duì)系統(tǒng)的內(nèi)核處理器數(shù)目比較敏感,至少需要多于一個(gè)的處理器,有幾個(gè)處理器就會(huì)開幾個(gè)線程(不過線程數(shù)是可以使用參數(shù)-XX:ParallelGCThreads=<N>控制的),因此只適合于多核多處理器的系統(tǒng)。盡管整個(gè)GC階段還是要暫停用戶程序,但多線程并行處理并不會(huì)造成太長的停頓時(shí)間。因此就吞吐量來說,ParNew要大于serial,在處理器越多的時(shí)候,效果越明顯。但是這并非絕對(duì),對(duì)于單個(gè)處理器來說,由于并行執(zhí)行的開銷(比如同步),ParNew的性能將會(huì)低于serial搜集器。不僅是單個(gè)處理器的時(shí)候,如果在容量較小的堆上,甚至在兩個(gè)處理器的情況下,ParNew的性能都并非一定可以高過serial。
? ? ? ? ?適用場(chǎng)景:在中到大型的堆上,且系統(tǒng)處理器至少多于一個(gè)的情況
? ? ? ? ?開啟參數(shù):-XX:+UseParNewGC
?
Parallel Scavenge Garbage Collector
? ? ? ? 這個(gè)搜集器與ParNew幾乎一模一樣,都是針對(duì)新生代設(shè)計(jì),采用復(fù)制算法的并行搜集器。它與ParNew最大的不同就是可設(shè)置的參數(shù)不一樣,它可以讓我們更精確的控制GC停頓時(shí)間以及吞吐量。
? ? ? ? parallel scavenge搜集器提供參數(shù)主要包括控制最大的停頓時(shí)間(使用-XX:MaxGCPauseMillis=<N>),以及控制吞吐量(使用-XX:GCTimeRatio=<N>)。由此可以看出,parallel scavenge就是為了提供吞吐量控制的搜集器。
? ? ? ? 不過千萬不要以為把最大停頓時(shí)間調(diào)的越小越好,或者吞吐量越大越好,在使用parallel scavenge搜集器時(shí),主要有三個(gè)性能指標(biāo),最大停頓時(shí)間、吞吐量以及新生代區(qū)域的最小值。
? ? ? ??parallel scavenge搜集器具有相應(yīng)的調(diào)節(jié)策略,它將會(huì)優(yōu)先滿足最大停頓時(shí)間的目標(biāo),次之是吞吐量,最后才是新生代區(qū)域的最小值。
? ? ? ? 因此,如果將最大停頓時(shí)間調(diào)的過小,將會(huì)犧牲整體的吞吐量以及新生代大小來滿足你的私欲。手心手背都是肉,我們最好還是不要這么干。不過parallel scavenge有一個(gè)參數(shù)可以讓parallel scavenge搜集器全權(quán)接手內(nèi)存區(qū)域大小的調(diào)節(jié),這其中還包括了晉升為年老代(可使用-XX:MaxTenuringThreshold=n調(diào)節(jié))的年齡,也就是使用-XX:UseAdaptiveSizePolicy打開內(nèi)存區(qū)域大小自適應(yīng)策略。
? ? ? ? parallel scavenge搜集器可使用參數(shù)-XX:+UseParallelGC開啟,同時(shí)它也是server模式下默認(rèn)的新生代搜集器。
?
Parallel Old Garbage Collector
? ? ? ? ?Parallel Old與ParNew或者Parallel Scavenge的關(guān)系就好似serial與serial old一樣,相互之間的區(qū)別并不大,只不過parallel old是針對(duì)年老代設(shè)計(jì)的并行搜集器而已,因此它采用標(biāo)記/整理算法。
? ? ? ? ?Parallel Old搜集器還有一個(gè)重要的意義就是,它是除了serial old以外唯一一個(gè)可以與parallel scavenge搭配工作的年老代搜集器,因此為了避免serial old影響parallel scavenge可控制吞吐量的名聲,parallel old就作為了parallel scavenge真正意義上的搭檔。
? ? ? ? ?它可以使用參數(shù)-XX:-UseParallelOldGC開啟,不過在JDK6以后,它也是在開啟parallel scavenge之后默認(rèn)的年老代搜集器。
?
Concurrent Mark Sweep Garbage Collector
? ? ? ? ?concurrent mark sweep(以下簡(jiǎn)稱CMS)搜集器是唯一一個(gè)真正意義上實(shí)現(xiàn)了應(yīng)用程序與GC線程一起工作(一起是針對(duì)客戶而言,而并不一定是真正的一起,有可能是快速交替)的搜集器。
? ? ? ? ?CMS是針對(duì)年老代設(shè)計(jì)的搜集器,并采用標(biāo)記/清除算法,它也是唯一一個(gè)在年老代采用標(biāo)記/清除算法的搜集器。
? ? ? ? ?采用標(biāo)記/清除算法是因?yàn)樗厥獾奶幚矸绞皆斐傻?#xff0c;它的處理分為四個(gè)階段。
? ? ? ? ?1、初始標(biāo)記:需要暫停應(yīng)用程序,快速標(biāo)記存活對(duì)象。
? ? ? ? ?2、并發(fā)標(biāo)記:恢復(fù)應(yīng)用程序,并發(fā)跟蹤GC Roots。
? ? ? ? ?3、重新標(biāo)記:需要暫停應(yīng)用程序,重新標(biāo)記跟蹤遺漏的對(duì)象。
? ? ? ? ?4、并發(fā)清除:恢復(fù)應(yīng)用程序,并發(fā)清除未標(biāo)記的垃圾對(duì)象。
? ? ? ? ?它比原來的標(biāo)記/清除算法復(fù)雜了點(diǎn),主要表現(xiàn)在并發(fā)標(biāo)記和并發(fā)清除這兩個(gè)階段,而這兩個(gè)階段也是整個(gè)GC階段中耗時(shí)最長的階段,不過由于這兩個(gè)階段皆是與應(yīng)用程序并發(fā)執(zhí)行的,因此CMS搜集器造成的停頓時(shí)間是非常短暫的。這點(diǎn)還是比較好理解的。
? ? ? ? ?不過它的缺點(diǎn)也是要簡(jiǎn)單提一下的,主要有以下幾點(diǎn)。
?
? ? ? ? ?1、由于GC線程與應(yīng)用程序并發(fā)執(zhí)行時(shí)會(huì)搶占CPU資源,因此會(huì)造成整體的吞吐量下降。也就是說,從吞吐量的指標(biāo)上來說,CMS搜集器是要弱于parallel scavenge搜集器的。LZ這里從oracle官網(wǎng)上摘錄下一段關(guān)于CMS的描述,里面提到CMS性能與CPU個(gè)數(shù)的關(guān)系。
? ? ? ? ?Since at least one processor is utilized for garbage collection during the concurrent phases, the concurrent collector does not normally provide any benefit on a uniprocessor (single-core) machine. However, there is a separate mode available that can achieve low pauses on systems with only one or two processors; see incremental mode?below for details.
? ? ? ? ?LZ的英文很一般(四級(jí)都沒過,慚愧,0.0),不過在借助工具的情況下也能大致翻譯出來這段話的意思,如下。
? ? ? ? ?中文大意:由于在并發(fā)階段垃圾搜集至少使用了一個(gè)處理器,因此在單處理器的情況下使用并發(fā)搜集器,將得不到任何好處。不過,在單個(gè)或兩個(gè)處理器的系統(tǒng)上,有一種獨(dú)立的方式可以有效的達(dá)到低停頓的目的,詳情見下方的增量模式(incremental mode)。
? ? ? ? ?很明顯,oracle的文檔指出,在單處理器的情況下,并發(fā)搜集器會(huì)因?yàn)閾屨继幚砥?#xff0c;而造成性能降低。最后給出了一種增量模式的處理方式,不過在《深入理解JAVA虛擬機(jī)》一書中指出,增量模式已經(jīng)被定義為不推薦使用。由于LZ摘錄的這段官方介紹是基于JDK5.0的介紹,而《深入理解JAVA虛擬機(jī)》一書中則是指的JDK6.0的版本,因此LZ暫且猜測(cè),增量模式是在JDK6.0發(fā)布的時(shí)候被廢棄了,不過這個(gè)廢棄的時(shí)間或者說版本其實(shí)已經(jīng)不重要了。
? ? ? ? ?2、標(biāo)記/清除很大的一個(gè)缺點(diǎn),那就是內(nèi)存碎片的存在。因此JVM提供了-XX:+UseCMSCompactAtFullCollection參數(shù)用于在全局GC(full GC)后進(jìn)行一次碎片整理的工作,由于每次全局GC后都進(jìn)行碎片整理會(huì)較大的影響停頓時(shí)間,JVM又提供了參數(shù)-XX:CMSFullGCsBeforeCompaction去控制在幾次全局GC后會(huì)進(jìn)行碎片整理。
? ? ? ? ?3、CMS最后一個(gè)缺點(diǎn)涉及到一個(gè)術(shù)語---并發(fā)模式失敗(Concurrent Mode Failure)。對(duì)于這個(gè)術(shù)語,官方是這樣解釋的。
? ? ? ? ?if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfied with the available free space blocks in the tenured generation, then the application is paused and the collection is completed with all the application threads stopped.The inability to complete a collection concurrently is referred to as?concurrent mode failure?and indicates the need to adjust the concurrent collector parameters.
? ? ? ? ?中文大意:如果并發(fā)搜集器不能在年老代填滿之前完成不可達(dá)(unreachable)對(duì)象的回收,或者年老代中有效的空閑內(nèi)存空間不能滿足某一個(gè)內(nèi)存的分配請(qǐng)求,此時(shí)應(yīng)用會(huì)被暫停,并在此暫停期間開始垃圾回收,直到回收完成才會(huì)恢復(fù)應(yīng)用程序。這種無法并發(fā)完成搜集的情況就成為并發(fā)模式失敗(concurrent mode failure),而且這種情況的發(fā)生也意味著我們需要調(diào)節(jié)并發(fā)搜集器的參數(shù)了。
? ? ? ? ?上面兩個(gè)情況感覺有點(diǎn)重復(fù),不能滿足內(nèi)存的分配請(qǐng)求不就是在年老代填滿之前,沒有完成對(duì)象回收造成的嗎?
? ? ? ? ?這里L(fēng)Z個(gè)人的理解是,年老代填滿之前無法完成對(duì)象回收是指年老代在并發(fā)清除階段清除不及時(shí),因此造成的空閑內(nèi)存不足。而不能滿足內(nèi)存的分配請(qǐng)求,則主要指的是新生代在提升到年老代時(shí),由于年老代的內(nèi)存碎片過多,導(dǎo)致一些分配由于沒有連續(xù)的內(nèi)存無法滿足。
? ? ? ? ?實(shí)際上,在并發(fā)模式失敗的情況下,serial old會(huì)作為備選搜集器,進(jìn)行一次全局GC(Full GC),因此serial old也算是CMS的“替補(bǔ)”。顯然,由于serial old的介入,會(huì)造成較大的停頓時(shí)間。
? ? ? ? ?為了盡量避免并發(fā)模式失敗發(fā)生,我們可以調(diào)節(jié)-XX:CMSInitiatingOccupancyFraction=<N>參數(shù),去控制當(dāng)年老代的內(nèi)存占用達(dá)到多少的時(shí)候(N%),便開啟并發(fā)搜集器開始回收年老代。
? ? ? ? ?
組合的威力
? ? ? ? ?上面我們已經(jīng)簡(jiǎn)單的介紹了各個(gè)搜集器的特點(diǎn),下面LZ與各位分享三個(gè)典型的組合,其余三種組合一般不常用。
?
serial & serial old
? ? ? ? ?這個(gè)組合是我們最常見的組合之一,也是client模式下的默認(rèn)垃圾搜集器組合,也可以使用參數(shù)-XX:+UseSerialGC強(qiáng)制開啟。
? ? ? ? ?由于它實(shí)現(xiàn)相對(duì)簡(jiǎn)單,沒有線程相關(guān)的額外開銷(主要指線程切換與同步),因此非常適合運(yùn)行于客戶端PC的小型應(yīng)用程序,或者桌面應(yīng)用程序(比如swing編寫的用戶界面程序),以及我們平時(shí)的開發(fā)、調(diào)試、測(cè)試等。
? ? ? ? ?上面三種情況都有共同的特點(diǎn)。
? ? ? ? ?1、由于都是在PC上運(yùn)行,因此配置一般不會(huì)太高,或者說處理器個(gè)數(shù)不會(huì)太多。
? ? ? ? ?2、上面幾種情況的應(yīng)用程序都不會(huì)運(yùn)行太久。
? ? ? ? ?3、規(guī)模不會(huì)太大,也就是說,堆相對(duì)較小,搜集起來也比較快,停頓時(shí)間會(huì)比較短。
? ? ? ? ?
Parallel Scavenge & Parallel Old
? ? ? ? ?這個(gè)組合我們并不常見,畢竟它不會(huì)出現(xiàn)在我們平時(shí)的開發(fā)當(dāng)中,但是它卻是很多對(duì)吞吐量(throughout)要求較高或者對(duì)停頓時(shí)間(pause time)要求不高的應(yīng)用程序的首選,并且這個(gè)組合是server模式下的默認(rèn)組合(JDK6或JDK6之后)。當(dāng)然,它也可以使用-XX:+UseParallelGC參數(shù)強(qiáng)制開啟。
? ? ? ? ?該組合無論是新生代還是年老代都采用并行搜集,因此停頓時(shí)間較短,系統(tǒng)的整體吞吐量較高。它適用于一些需要長期運(yùn)行且對(duì)吞吐量有一定要求的后臺(tái)程序。
? ? ? ? ?這些運(yùn)行于后臺(tái)的程序都有以下特點(diǎn)。
? ? ? ? ?1、系統(tǒng)配置較高,通常情況下至少四核(以目前的硬件水平為準(zhǔn))。
? ? ? ? ?2、對(duì)吞吐量要求較高,或需要達(dá)到一定的量。
? ? ? ? ?3、應(yīng)用程序運(yùn)行時(shí)間較長。
? ? ? ? ?4、應(yīng)用程序規(guī)模較大,一般是中到大型的堆。
?
ParNew & CMS(Serial Old作為替補(bǔ))
? ? ? ? ?這個(gè)組合與上面的并行組合一樣,在平時(shí)的開發(fā)當(dāng)中都不常見,而它則是對(duì)相應(yīng)時(shí)間(response time)要求較高的應(yīng)用程序的首選。該組合需要使用參數(shù)-XX:+UseConcMarkSweepGC開啟。
? ? ? ? ?該組合在新生代采用并行搜集器,因此新生代的GC速度會(huì)非常快,停頓時(shí)間很短。而年老代的GC采用并發(fā)搜集,大部分垃圾搜集的時(shí)間里,GC線程都是與應(yīng)用程序并發(fā)執(zhí)行的,因此造成的停頓時(shí)間依然很短。它適用于一些需要長期運(yùn)行且對(duì)相應(yīng)時(shí)間有一定要求的后臺(tái)程序。
? ? ? ? ?這些運(yùn)行于后臺(tái)的程序的特點(diǎn)與并行模式下的后臺(tái)程序十分類似,不同的是第二點(diǎn),采用ParNew & CMS組合的后臺(tái)應(yīng)用程序,一般都對(duì)相應(yīng)時(shí)間有一定要求,最典型的就是我們的WEB應(yīng)用程序。
?
結(jié)束語
? ? ? ? ?本次LZ整理了各個(gè)搜集器的特點(diǎn)與各個(gè)組合的特點(diǎn),此外,還有剩下的三種組合LZ這里沒有提到,原因是這三種組合都不是特別常用,或者可以說幾乎不用,因?yàn)檫@三個(gè)組合都給人一種四不像的感覺,而且效果也確實(shí)不好。
總結(jié)
以上是生活随笔為你收集整理的JVM内存管理------垃圾搜集器精解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 滴滴自动驾驶货运业务 KargoBot
- 下一篇: 雷军确认小米14已经开始生产!首发小米澎