JVM學(xué)習(xí) 之 垃圾收集器

本篇是JVM的第一篇學(xué)習(xí)筆記摹芙,主要學(xué)習(xí)書籍是《深入理解Java虛擬機(jī) 之JVM高級特性與最佳實踐》驮吱,總結(jié)歸納自己的學(xué)習(xí)心得阳距。

目錄

JVM學(xué)習(xí) 之 垃圾收集器
1免绿、 Java 內(nèi)存區(qū)域
1.1凡伊、程序計數(shù)器
1.2零渐、虛擬機(jī)棧
1.3、本地方法棧
1.4系忙、Java堆
1.5诵盼、方法區(qū)
1.6、運行時常量池
1.7银还、直接內(nèi)存
2风宁、垃圾回收器
2.1、對象
2.1.1蛹疯、可達(dá)性分析算法
2.1.2戒财、引用類型
2.1.3、方法區(qū)回收
2.2捺弦、垃圾收集算法
2.2.1饮寞、標(biāo)記-清除算法
2.2.2、復(fù)制算法
2.2.3列吼、標(biāo)記-整理算法
2.2.4幽崩、分代收集算法
2.3、垃圾收集器
2.3.1寞钥、Serial 收集器
2.3.2慌申、ParNew 收集器
2.3.3、Parallel Scavenge 收集器
2.3.4理郑、Serial Old 收集器
2.3.5蹄溉、Parallel Old 收集器
2.3.6、CMS 收集器
2.3.7您炉、G1 收集器
2.4柒爵、常用參數(shù)
3、后續(xù)

1赚爵、 Java 內(nèi)存區(qū)域

在C餐弱、C++開發(fā)中宴霸,由開發(fā)者自行管理所有對象的內(nèi)存申請與釋放,但是Java有自己的內(nèi)存管理機(jī)制膏蚓,在絕大部分場景下可以自動完成內(nèi)存的申請和內(nèi)存釋放,不太容易出現(xiàn)內(nèi)存泄露和內(nèi)存溢出的問題

  • 內(nèi)存溢出Out Of Memory(OOM):直接導(dǎo)致的問題就是內(nèi)存不夠用畸写,例如需要申請10M的內(nèi)存驮瞧,現(xiàn)在只有5M的可用內(nèi)存,就會提示OOM錯誤枯芬,也就是內(nèi)存溢出
  • 內(nèi)存泄露Memory Leak:申請的內(nèi)存空間沒有很好的釋放回收论笔,經(jīng)過多次內(nèi)存泄露操作,就可能轉(zhuǎn)變?yōu)镺OM

在Java程序執(zhí)行過程中千所,虛擬機(jī)會把管理的內(nèi)存根據(jù)不同的需求劃分為若干不同的區(qū)域狂魔,如下圖是《Java 虛擬機(jī)規(guī)范(Java SE 7版)》規(guī)定所包含的各種數(shù)據(jù)區(qū)域

image

1.1、程序計數(shù)器

程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間淫痰,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器最楷。字節(jié)碼解釋器就是通過修改該數(shù)據(jù)選取下一條需要執(zhí)行的指令,包含循環(huán)待错、跳轉(zhuǎn)籽孙、分支、異常處理等情況火俄。

每一個線程獨有單獨的程序計數(shù)器犯建,每一個處理器在特定的時刻也只會運行一個線程。

如果執(zhí)行的是Java方法瓜客,記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址适瓦。如果是native方法,計數(shù)器為空(Undefined)谱仪。此內(nèi)存區(qū)域是唯一一個在java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域玻熙。

1.2、虛擬機(jī)棧

虛擬機(jī)棧(Java Virtual Machine Stacks)同樣是線程私有芽卿,和線程有著同樣的生命周期揭芍。

每一個方法在執(zhí)行時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧卸例、方法接口等信息称杨,每一個方法從開始調(diào)用到完成的過程就是一個棧幀在虛擬機(jī)棧中入棧和出棧的過程。其中局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型筷转。局部變量表所需的空間在編譯期完成分配姑原,一個32位類型的數(shù)據(jù)占用一個局部變量空間(Slot)。

  • StackOverflowError:棧溢出錯誤呜舒,線程請求的棧深度超過了虛擬機(jī)所允許的深度锭汛,一般很難出現(xiàn)棧溢出錯誤,除非在代碼中出現(xiàn)了循環(huán)申請棧的情況。
  • OutOfMemoryError:上面已經(jīng)說了內(nèi)存溢出是因為內(nèi)存不夠用導(dǎo)致的

如何編寫棧溢出錯誤的代碼呢唤殴?很簡單般婆,我們上面已經(jīng)說了每一個方法被調(diào)用就是入棧的操作,那么循環(huán)調(diào)用一個方法朵逝,不讓其出棧蔚袍,就可以出現(xiàn)棧溢出的錯誤了。具體如下圖配名,棧深度達(dá)到了近1w8

image

1.3啤咽、本地方法棧

本地方法棧(Native Method Stack)和虛擬機(jī)棧類似,但是本地方法棧則是服務(wù)于虛擬機(jī)使用到的native方法渠脉,例如一般的lib方法等宇整,和虛擬機(jī)棧類似,同樣會出現(xiàn)棧溢出和內(nèi)存溢出的情況芋膘。

1.4鳞青、Java堆

Java堆是所有線程共享的內(nèi)存區(qū)域,幾乎所有的對象實例都是在這里分配內(nèi)存索赏。Java堆是垃圾收集器管理的主要區(qū)域盼玄,也被成為GC堆(Garbage Collected Heap),細(xì)分區(qū)域可以分為新生代和老年代潜腻;再細(xì)致一點則有Eden空間(伊甸區(qū))埃儿,F(xiàn)orm Survivor、To Survivor等空間融涣。

從內(nèi)存分配的角度出發(fā)童番,線程共享的Java堆可能劃分出多個線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)

1.5威鹿、方法區(qū)

方法區(qū)(Method Area)和Java堆一樣剃斧,是由各個線程所共享的區(qū)域,被用來存儲已經(jīng)被虛擬機(jī)加載的類信息忽你、常量幼东、靜態(tài)變量、即時編譯的數(shù)據(jù)等信息科雳。

在HotSpot虛擬機(jī)上根蟹,更多人原因把方法去稱為永久代(Permanent Generation),當(dāng)內(nèi)存不足時糟秘,則拋出OOM錯誤

1.6简逮、運行時常量池

運行時常量池(Runtime Constant Pool)是方法區(qū)的一部分,用來存放各種生成的字面量尿赚,例如定義的一個String類型的數(shù)據(jù)散庶,最后就被存在此處蕉堰,同樣的也會出現(xiàn)OOM錯誤

1.7、直接內(nèi)存

直接內(nèi)存(Direct Memory),不是虛擬機(jī)運行時區(qū)域內(nèi)的數(shù)據(jù)悲龟,可以利用native方法屋讶,直接分配堆外內(nèi)存,然后可以通過DirectoryByetBuffer對象對該數(shù)據(jù)進(jìn)行讀寫操作躲舌,在NIO中利用該種方法可以極大的提高數(shù)據(jù)傳輸丑婿,大小不受虛擬機(jī)控制,但是會由機(jī)器的內(nèi)存大小没卸、處理器等因素印象。

2秒旋、垃圾回收器

了解和學(xué)習(xí)垃圾收集器约计,了解GC過程、內(nèi)存分配便于我們實際解決各種內(nèi)存溢出迁筛、內(nèi)存泄露的問題煤蚌,雖然絕大部分問題都已經(jīng)由自動化的內(nèi)存動態(tài)分配和內(nèi)存回收機(jī)制解決掉了。

GC過程需要考慮的三個問題

  • 哪些內(nèi)存需要回收
  • 什么時候回收
  • 如何回收

哪些內(nèi)存需要回收细卧,肯定是已經(jīng)消亡的尉桩,生命周期結(jié)束了,已經(jīng)不需要使用的對象贪庙。那么怎么判斷對象是否真的是不需要使用的呢蜘犁?

2.1、對象

2.1.1止邮、可達(dá)性分析算法

在堆中存放了JVM中幾乎所有的對象實例这橙,如何確認(rèn)哪些對象是存活的還是消亡的,很多語言都是使用了引用計數(shù)器导披,當(dāng)一個對象被引用一次就使得其對象的引用計數(shù)器+1屈扎,直到引用計數(shù)器為0,則認(rèn)為當(dāng)前對象沒有被引用撩匕,可以被回收掉鹰晨。

之前我也是這樣認(rèn)為的,python也是類似的解決方案止毕。但是java卻沒有使用該方法去管理內(nèi)存模蜡,主要原因是其很難解決對象之間的相互引用的問題,例如ObjectA 引用了ObjectB滓技,同時ObjectB也引用了ObjectA哩牍,那么就出現(xiàn)了相互持有的階段,如果是引用計數(shù)器方法令漂,意味著這兩個對象永遠(yuǎn)不能被回收

可達(dá)性分析算法

在Java中使用的是叫可達(dá)性分析算法膝昆,這個算法是通過一系列成為GC Roots的對象作為起點丸边,開始進(jìn)行搜索操作,搜索的路徑成為引用鏈荚孵,當(dāng)一個對象到GC Roots沒有任何引用鏈妹窖,也就是不可達(dá),則證明此對象可以被回收收叶。如下圖骄呼,對象C和對象D是獨立存在的,將會在搜索完成之后被判定為可回收對象

image

在Java中判没,可以作為GC Roots的對象包括為如下幾種

  • 虛擬機(jī)棧中引用的對象
  • 方法區(qū)中類靜態(tài)屬性引用的對象
  • 方法區(qū)中常量引用的對象
  • 本地方法中JNI引用的對象

2.1.2蜓萄、引用類型

在我們一般的理解中引用就是一個reference類型的數(shù)據(jù)所存儲的值是另一塊內(nèi)存的起始地址,例如Object obj = new Object()這句代碼澄峰,obj就是一個引用對象嫉沽,指向著new Object()對象。在JDK1.2之后俏竞,Java對引用的概念進(jìn)行了擴(kuò)充绸硕,講引用分為了強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)魂毁、弱引用(Weak Reference)玻佩、虛引用(Phantom Reference),這四種引用強(qiáng)度依次逐漸減落席楚。

  • 強(qiáng)引用:上面說的obj就是強(qiáng)引用咬崔,只要強(qiáng)已用一直在,垃圾收集器永遠(yuǎn)不會回收被引用的對象
  • 軟引用:用來描述一些有用但不是必須的對象酣胀,在OOM之前刁赦,會將該類型對象放進(jìn)回收范圍內(nèi)進(jìn)行二次回收操作,如果這次回收之后還是沒有足夠的內(nèi)存闻镶,才會發(fā)生OOM甚脉,現(xiàn)提供了SoftReference類提供使用
  • 弱引用:比軟引用的強(qiáng)度更低,被關(guān)聯(lián)的對象在下一次垃圾回收的時候就會被回收掉铆农,提供了WeakReference類實現(xiàn)弱引用
  • 虛引用:也稱為幽靈引用或者幻影引用牺氨,一個對象是否存在虛引用的存在完全不會對其生存時間產(chǎn)生影響,也無法通過虛引用來取得一個對象實例墩剖。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能再這個對象被收集器回收的時候會收到一個系統(tǒng)通知猴凹,提供了PhantomReference類實現(xiàn)虛引用

2.1.3、方法區(qū)回收

在Java規(guī)范中確實說了不要氣虛擬機(jī)在方法區(qū)實現(xiàn)垃圾回收岭皂,而且在方法區(qū)進(jìn)行垃圾回收的性價比比較低郊霎。一般情況下,常規(guī)應(yīng)用進(jìn)行一次垃圾收集操作就會回收絕大部分的空間爷绘,在方法區(qū)進(jìn)行垃圾收集的效率確實低于此书劝。

在永久代中垃圾收集主要是回收兩部分內(nèi)容:廢棄變量和無用的類进倍,那么如何判斷一個類是否是無用的呢?

  • 該類所有的實例已經(jīng)被回收购对,在JVM中不存在該類的任何實例
  • 加載該類的ClassLoader也已經(jīng)被回收了
  • 該類對應(yīng)的java.lang.Class 對象沒有在任何地方被引用猾昆,無法在任何地方通過反射訪問該類方法

2.2、垃圾收集算法

2.2.1骡苞、標(biāo)記-清除算法

Mark-Sweep算法垂蜗,包含了標(biāo)記和清除兩個階段

  • 通過GC Roots方法,區(qū)分存活解幽、將要被回收的對象贴见,然后標(biāo)記將要被回收的對象
  • 統(tǒng)一回收所有被標(biāo)記的對象

最基礎(chǔ)的清除算法,后續(xù)的算法都是對其進(jìn)行優(yōu)化的結(jié)果

主要的不足是

  • 效率問題:標(biāo)記和清除操作的效率不高躲株,需要暫停用戶線程蝇刀,而且需要遞歸遍歷全堆的數(shù)據(jù)
  • 空間問題:完成標(biāo)記清除后,會產(chǎn)生大量的不連續(xù)的內(nèi)存碎片徘溢,如果后續(xù)需要插入連續(xù)的大塊內(nèi)存,沒有足夠的連續(xù)空間捆探,則會引發(fā)新的垃圾手機(jī)操作

2.2.2然爆、復(fù)制算法

復(fù)制算法的操作原理是標(biāo)記出存活的對象,然后把其存活的對象拷貝至其他內(nèi)存空間黍图,然后對之前的整個內(nèi)存空間進(jìn)行回收曾雕,而且在內(nèi)存分配的時候,不用考慮內(nèi)存碎片的情況助被,移動指針剖张,按順序分配內(nèi)存,不過這種算法對空間的使用率會偏低揩环,他必須在空間使用到一定比率的時候就開始進(jìn)行垃圾收集操作

在HotSpot虛擬機(jī)中搔弄,將內(nèi)存塊分配為一塊較大的Eden空間,還有兩塊較小的Survivor空間丰滑,每次都是使用Eden和其中的一塊Survivor空間顾犹,最后把存活的對象拷貝至另一塊未使用的Survivor空間,而對Eden和使用中的Survivor進(jìn)行垃圾收集褒墨。默認(rèn)情況Eden和Survivor的大小比率是8:1炫刷,這就意味著真正使用的內(nèi)存空間是90%,剩下的10%就是被浪費掉的(處于不被使用的情況)

那么這就存在一個問題了郁妈,如果存活的對象空間超過了10%浑玛,那么超出的對象講通過分配擔(dān)保機(jī)制存放到老年代(也就是上面說的方法區(qū))

該方法適用于朝生夕死的服務(wù),對象的存活率較低的情況下噩咪,使得復(fù)制的工作最少顾彰,效率達(dá)到最高

2.2.3极阅、標(biāo)記-整理算法

Mark-Compact算法,標(biāo)記過程和標(biāo)記清除算法相同拘央,但是后續(xù)步驟是把所有存活的對象移動到另一端涂屁,然后直接清除邊界外的內(nèi)存。

2.2.4灰伟、分代收集算法

現(xiàn)在的商業(yè)虛擬機(jī)的垃圾收集器都是采用分代收集(Generational Collection)算法拆又,一般情況下把堆分為新生代和老年代,根據(jù)不同的年代特點使用最合適的收集算法栏账。如果每次垃圾收集后的對象存活率很低帖族,那使用復(fù)制算法效率最高,而老年代的對象存活率高挡爵,而且沒有額外的空間對其進(jìn)行分配擔(dān)保竖般,就必須使用標(biāo)記-清除或者標(biāo)記-整理算法進(jìn)行回收操作。

2.3茶鹃、垃圾收集器

垃圾收集器是垃圾回收算法的具體實現(xiàn)涣雕,一般情況下不同的廠商有著不同的垃圾收集器,主要學(xué)習(xí)的是HotSpot虛擬機(jī)的收集器關(guān)聯(lián)關(guān)系闭翩,具體如下圖挣郭。一個新生代的收集器和一個老年代的收集器相結(jié)合,用直線連接的收集器才能夠配套使用疗韵,例如Serial不能和Parallel Old收集器一起使用兑障。

image

不過有一個觀點是必須接受的,目前不存在最好的收集器蕉汪,更沒有萬能的收集器流译,只有在特定場景下的相對合適的收集器,需要結(jié)合具體應(yīng)用場景。

接下來就具體學(xué)習(xí)上面的幾種收集器,學(xué)習(xí)他們的使用原理吱肌,優(yōu)點和缺點,適用在什么樣的場景下

2.3.1竞漾、Serial 收集器

早起最基礎(chǔ)的新生代垃圾收集器,采用的是復(fù)制算法窥翩,是單一線程的收集器业岁,在進(jìn)行垃圾回收時,必須暫停其他所有工作線程的運行寇蚊,直到收集結(jié)束笔时。線程暫停也就是俗稱的“Stop The World”,早起他是和老年代的Serial Old 一起工作完成收集的任務(wù)仗岸。如下圖是Serial & Serial Old 收集器運行示意圖

image

虛擬機(jī)在Client模式下默認(rèn)的新生代收集器允耿。對于限定單個CPU的環(huán)境來說借笙,簡單高效,Serial收集器由于沒有線程交互的開銷较锡,專心做垃圾收集自然可以獲得最高的單線程收集效率业稼。所有Serial收集器對于運行在Client模式下的虛擬機(jī)是一個很好的選擇

2.3.2、ParNew 收集器

Serial 收集器的多線程版本蚂蕴,同樣是復(fù)制算法低散,在進(jìn)行收集操作時,可以啟用多個線程同時進(jìn)行回收操作骡楼。

image

ParNew收集器在單CPU環(huán)境下絕對不會有比Serial收集器更好的效果熔号,由于存在線程之間的同步切換等開銷,該收集器在兩個CPU環(huán)境中也未必就可以使得效率超過Serial收集器鸟整,可以使用-XX:parallelGCThreads參數(shù)來限制收集器線程數(shù)

2.3.3引镊、Parallel Scavenge 收集器

同樣是新生代的收集器,也是采用的復(fù)制算法篮条,但是和ParNew收集器還是存在諸多不同的地方

image

Parallel Scavenge收集器主要的特點是達(dá)到一個可控制的吞吐量弟头,吞吐量就是CPU用于運行用戶代碼的時間和CPU總耗時間的對比值,如果在100分鐘內(nèi)涉茧,垃圾回收花費了1分鐘亮瓷,則吞吐量就是99%,停頓時間越短越適合于需要與用戶交互的程序降瞳,高吞吐量則可以充分的使用CPU時間,適合用于在后臺運算而不需要太多的交互任務(wù)蚓胸。

Parallel Scavenge收集器提供了兩個參數(shù)來精確控制吞吐量:

  • -XX:MaxGCPauseMillis 最大垃圾收集器停頓時間(大于0的毫秒數(shù)挣饥,停頓時間小了就要犧牲相應(yīng)的吞吐量和新生代空間)
  • -XX:GCTimeRatio 吞吐量大小(大于0小于100的整數(shù)沛膳,默認(rèn)99扔枫,也就是允許最大1%的垃圾回收時間)

因為和吞吐量有著密切的關(guān)系,也被稱為吞吐量優(yōu)先收集器锹安,除了上述兩個參數(shù)外短荐,還有一個開關(guān)參數(shù)-XX:+UseAdaptiveSizePolicy ,當(dāng)這個參數(shù)打開后,不需要手工指定新生代的大小-Xmn和Eden與Survivor的比例-XX:SurvivorRatio叹哭、晉升老年代對象大小-XX:PretenureSizeThreshold等細(xì)節(jié)參數(shù)忍宋,虛擬機(jī)會根據(jù)當(dāng)前的吞吐量自行適配以達(dá)到最合適的停頓時間或者最大的吞吐量

自適應(yīng)調(diào)節(jié)策略也是Parallel Scavenge 收集器和ParNew 收集器的區(qū)別之一

2.3.4、Serial Old 收集器

Serial收集器對應(yīng)的老年代收集器风罩,同樣是單線程工作糠排,使用標(biāo)記-整理算法,具體如下圖所示

image

2.3.5超升、Parallel Old 收集器

是Parallel Savenge 收集器的老年代版本實現(xiàn)入宦,使用多線程和整理-標(biāo)記算法哺徊,從JDK1.6才開始提供的。

image

2.3.6乾闰、CMS 收集器

CMS(Concurrent Mark Sweep)收集器是一種獲取最短回收停頓時間為目標(biāo)的收集器落追,主要應(yīng)用在服務(wù)端,加快響應(yīng)速度涯肩,基于標(biāo)記-清除算法轿钠,他的運行主要是包含了4個步驟

  • 初始標(biāo)記(CMS initial mark)
  • 并發(fā)標(biāo)記(CMS concurrent mark)
  • 重新標(biāo)記(CMS remark)
  • 并發(fā)清除(CMS concurrent sweep)

其中初始標(biāo)記、重新標(biāo)記都是需要“Stop The World”宽菜,也就是暫停用戶線程的執(zhí)行谣膳。

  • 初始標(biāo)記僅僅是標(biāo)記通過GC Roots能直接關(guān)聯(lián)的對象,速度很快
  • 并發(fā)標(biāo)記就是進(jìn)行GC Roots Tracing的過程铅乡,標(biāo)記出所有的對象继谚,會判斷對象是否存活,可以和用戶線程同步進(jìn)行
  • 重新標(biāo)記則是為了修正在并發(fā)標(biāo)記階段因用戶線程執(zhí)行導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象標(biāo)記記錄
  • 并發(fā)清除是耗時比較重要的操作阵幸,完成上述標(biāo)記的需要被清除的對象的清除操作
image

CMS收集器的優(yōu)點:并發(fā)收集低停頓花履,但是也有幾個缺點

  • 對CPU資源非常敏感:默認(rèn)的回收線程個數(shù)是(CPU數(shù)量+3) / 4,垃圾回收器始終不會占據(jù)低于25%的CPU資源挚赊,當(dāng)CPU個數(shù)不足四個的時候诡壁,例如兩個還需要分配近50%的CPU能力去執(zhí)行收集器線程,這會極大的消耗CPU的有效資源
  • 無法處理浮動的垃圾:因為在并發(fā)清理階段不會暫停用戶線程荠割,這其中就會產(chǎn)生新的額外的未回收垃圾妹卿,只能等到下一次GC時再清理,同時由于CMS收集器在垃圾收集階段蔑鹦,用戶線程還在運行夺克,那還需要預(yù)留足夠的內(nèi)存空間提供給用戶線程使用,因此CMS收集器不和其他收集器一般嚎朽,等到?jīng)]有內(nèi)存了再進(jìn)行收集操作铺纽,需要預(yù)留一部分空間提供給并發(fā)收集過程中的用戶線程使用,在JDK1.5中哟忍,CMS收集器在老年代空間使用到了68%時狡门,就會被激活,可以通過設(shè)置參數(shù)-XX:CMSInitiatingOccupancyFraction提供觸發(fā)百分比锅很,在JDK1.6中其馏,該值默認(rèn)修改為92%了,如果再CMS運行期間預(yù)留的內(nèi)存無法滿足用戶線程的執(zhí)行爆安,會出現(xiàn)Concurrent Mode Failure錯誤尝偎,將會導(dǎo)致啟動Full GC 操作。實際生產(chǎn)環(huán)境中需要注意該參數(shù),如果該參數(shù)過大很容易導(dǎo)致大量的Concurrent Mode Failure失敗致扯,反而會影響性能
  • 標(biāo)記清除算法本身的缺點:上文可知標(biāo)記清除算法會導(dǎo)致大量的空間碎片肤寝,一旦沒有足夠的連續(xù)空間分配給大對象,則會提前出發(fā)Full GC 操作抖僵,為了解決該問題鲤看,CMS收集器提供了一個開關(guān)參數(shù)-XX:UseCMSCompactAtFullCollection,用戶在CMS進(jìn)行Full GC操作時進(jìn)行的內(nèi)存碎片合并整理的過程耍群,這個階段不是并發(fā)的义桂,會使得停頓時間加長,還提供了另一個參數(shù)-XX:CMSFullGCBeforeCompaction蹈垢,用來設(shè)置執(zhí)行了多少次不壓縮的Full GC后緊接著來一次帶壓縮的Full GC 操作慷吊,默認(rèn)為0,表示每次進(jìn)入Full GC操作時都進(jìn)行碎片整理曹抬。

2.3.7溉瓶、G1 收集器

G1(Garbage First)收集器是當(dāng)前最完善的收集器,在JDK1.7中可用谤民,未來可用替換掉CMS收集器堰酿,其特點包含了

  • 并行與并發(fā):充分利用多CPU、多核環(huán)境下的硬件優(yōu)勢张足,使用多個CPU和縮短Stop The World停頓的時間
  • 分代收集:分代概念依舊保留触创,能夠采用不同方式去處理新建對象和已經(jīng)存活了一段時間、熬過多次GC的老年代對象以獲取更好收集效果
  • 空間整合:整體是基于標(biāo)記-整理的算法为牍,但是局部來看則是采用了復(fù)制算法實現(xiàn)哼绑,這兩種算法都不會產(chǎn)生內(nèi)存空間碎片,收集后可提供規(guī)整的可用內(nèi)存碉咆,有利于程序長時間運行凌那,分配大對象不會因為沒有可用內(nèi)存而提前觸發(fā)GC操作
  • 可預(yù)測的停頓:G1除了追求低停頓之外,還能建立可預(yù)測的停頓時間模型吟逝,能讓使用者明確指定在一個長度為M毫秒的時間片段內(nèi),消耗在垃圾收集上的時間卻不得超過N毫秒

特點說明:

  • G1之前的收集器收集的范疇是整個新生代或者老年代赦肋,G1收集器則是把整個Java堆劃分為多個大小相等的獨立區(qū)域Region块攒,新生代和老年代不再是物理概念上的隔離,都是一部分Region的集合
  • G1之所以可以建立可預(yù)測的停頓時間模型佃乘,是因為其可以有計劃的避免在整個的Java堆中進(jìn)行全區(qū)域的垃圾收集囱井,他會跟蹤各個Region里面的垃圾值大小,在后臺維護(hù)一個優(yōu)先隊列趣避,每次根據(jù)允許的時間庞呕,優(yōu)先回收垃圾值最大的Region,這樣可以有效的保證了G1收集器可以在有限的時間內(nèi)獲取盡可能高的收集效率
  • G1對內(nèi)存化整為零的思路,雖然把內(nèi)存劃分為多個Region后住练,垃圾就完全按照Region為單位進(jìn)行垃圾回收地啰,Region本身是虛構(gòu)的概念,和實際的物理區(qū)域沒有太多的關(guān)系讲逛,必然存在某一個對象可能被多個毫無關(guān)聯(lián)的Region所引用亏吝,這樣就會導(dǎo)致對整個的Java堆的全局掃描,這明顯是不能被接受的盏混!為此在G1收集器中蔚鸥,Region之間的對象引用,以及其他收集器中新生代和老年代的對象引用许赃,虛擬機(jī)都是使用Remembered Set來避免全堆掃描止喷。G1中每個Region都有一個與之對應(yīng)的Remembered Set,虛擬機(jī)發(fā)現(xiàn)程序在對Reference類型的數(shù)據(jù)進(jìn)行寫操作時混聊,會產(chǎn)生一個Write Barrier暫時中斷寫操作弹谁,檢查Reference引用的對象是否處于不同的Region之中(分代的例子中就檢查是否老年代對象引用了新生代的對象),如果是則通過CardTable把相關(guān)引用信息記錄到被引用對象所屬的Region的Remembered Set之中技羔,當(dāng)進(jìn)行內(nèi)存回收時僵闯,在GC根節(jié)點的枚舉范圍中加入Remembered Set即可避免全堆掃描。

其主要運行的步驟分為

  • 初始標(biāo)記(Initial Marking)
  • 并發(fā)標(biāo)記(Concurrenr Marking)
  • 最終標(biāo)記(Final Marking)
  • 篩選回收(Live Data Counting And Evacution)
image

其中在最終標(biāo)記階段則是為了修正在并發(fā)標(biāo)記階段因用戶線程執(zhí)行導(dǎo)致標(biāo)記發(fā)生變化的那一部分的標(biāo)記情況藤滥,這段時間內(nèi)的變化將記錄在Remembered Set Logs中鳖粟,最終標(biāo)記的時候會把Remembered Set Logs的記錄合并到Remembered Set中,需要暫停用戶線程拙绊,但是可以并行執(zhí)行

最后的篩選回收階段向图,G1收集器會評估各個Region的垃圾值,根據(jù)用戶希望的GC停頓時間制定回收計劃标沪,因為停頓的只有一部分Region榄攀,所以可以大范圍的提高回收效率。

2.4金句、常用參數(shù)

這些常見的參數(shù)以及使用特定還是需要熟悉的

參數(shù) 描述
-XX:+UseSerialGC Jvm運行在Client模式下的默認(rèn)值檩赢,打開此開關(guān)后,使用Serial + Serial Old的收集器組合進(jìn)行內(nèi)存回收
-XX:+UseParNewGC 打開此開關(guān)后违寞,使用ParNew + Serial Old的收集器進(jìn)行垃圾回收
-XX:+UseConcMarkSweepGC 使用ParNew + CMS + Serial Old的收集器組合進(jìn)行內(nèi)存回收贞瞒,Serial Old作為CMS出現(xiàn)“Concurrent Mode Failure”失敗后的后備收集器使用
-XX:+UseParallelGC Jvm運行在Server模式下的默認(rèn)值,打開此開關(guān)后趁曼,使用Parallel Scavenge + Serial Old的收集器組合進(jìn)行回收
-XX:+UseParallelOldGC 使用Parallel Scavenge + Parallel Old的收集器組合進(jìn)行回收
-XX:SurvivorRatio 新生代中Eden區(qū)域與Survivor區(qū)域的容量比值军浆,默認(rèn)為8,代表Eden:Subrvivor = 8:1
-XX:PretenureSizeThreshold 直接晉升到老年代對象的大小挡闰,設(shè)置這個參數(shù)后乒融,大于這個參數(shù)的對象將直接在老年代分配
-XX:MaxTenuringThreshold 晉升到老年代的對象年齡掰盘,每次Minor GC之后,年齡就加1赞季,當(dāng)超過這個參數(shù)的值時進(jìn)入老年代
-XX:UseAdaptiveSizePolicy 動態(tài)調(diào)整java堆中各個區(qū)域的大小以及進(jìn)入老年代的年齡
-XX:+HandlePromotionFailure 是否允許新生代收集擔(dān)保愧捕,進(jìn)行一次minor gc后, 另一塊Survivor空間不足時,將直接會在老年代中保留
-XX:ParallelGCThreads 設(shè)置并行GC進(jìn)行內(nèi)存回收的線程數(shù)
-XX:GCTimeRatio GC時間占總時間的比列碟摆,默認(rèn)值為99晃财,即允許1%的GC時間,僅在使用Parallel Scavenge 收集器時有效
-XX:MaxGCPauseMillis 設(shè)置GC的最大停頓時間典蜕,在Parallel Scavenge 收集器下有效
-XX:CMSInitiatingOccupancyFraction 設(shè)置CMS收集器在老年代空間被使用多少后出發(fā)垃圾收集断盛,默認(rèn)值為68%,僅在CMS收集器時有效愉舔,-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSCompactAtFullCollection 由于CMS收集器會產(chǎn)生碎片钢猛,此參數(shù)設(shè)置在垃圾收集器后是否需要一次內(nèi)存碎片整理過程,僅在CMS收集器時有效
-XX:+CMSFullGCBeforeCompaction 設(shè)置CMS收集器在進(jìn)行若干次垃圾收集后再進(jìn)行一次內(nèi)存碎片整理過程轩缤,通常與UseCMSCompactAtFullCollection參數(shù)一起使用
-XX:+UseFastAccessorMethods 原始類型優(yōu)化
-XX:+DisableExplicitGC 是否關(guān)閉手動System.gc
-XX:+CMSParallelRemarkEnabled 降低標(biāo)記停頓
-XX:LargePageSizeInBytes 內(nèi)存頁的大小不可設(shè)置過大命迈,會影響Perm的大小,-XX:LargePageSizeInBytes=128m

3火的、后續(xù)

在17年新推出的Java9壶愤,就開始把G1收集器作為默認(rèn)的收集器,CMS不再被推薦使用
在Java 7和Java8中的默認(rèn)收集器是擁有高吞吐能力的Parallel GC收集器
G1是一個擁有可以預(yù)測停頓時間模型的這么一個收集器馏鹤,那么其優(yōu)點必然就是低延遲了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末征椒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子湃累,更是在濱河造成了極大的恐慌勃救,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件治力,死亡現(xiàn)場離奇詭異蒙秒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宵统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門晕讲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人马澈,你說我怎么就攤上這事瓢省。” “怎么了箭券?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疑枯。 經(jīng)常有香客問我辩块,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任废亭,我火速辦了婚禮国章,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豆村。我一直安慰自己液兽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布掌动。 她就那樣靜靜地躺著四啰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粗恢。 梳的紋絲不亂的頭發(fā)上柑晒,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音眷射,去河邊找鬼匙赞。 笑死,一個胖子當(dāng)著我的面吹牛妖碉,可吹牛的內(nèi)容都是我干的涌庭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼欧宜,長吁一口氣:“原來是場噩夢啊……” “哼坐榆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鱼鸠,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猛拴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚀狰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愉昆,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年麻蹋,在試婚紗的時候發(fā)現(xiàn)自己被綠了跛溉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡扮授,死狀恐怖芳室,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刹勃,我是刑警寧澤堪侯,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站荔仁,受9級特大地震影響伍宦,放射性物質(zhì)發(fā)生泄漏芽死。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一次洼、第九天 我趴在偏房一處隱蔽的房頂上張望关贵。 院中可真熱鬧,春花似錦卖毁、人聲如沸揖曾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炭剪。三九已至,卻和暖如春禁悠,著一層夾襖步出監(jiān)牢的瞬間念祭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工碍侦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留粱坤,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓瓷产,卻偏偏與公主長得像站玄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子濒旦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容