在Java運行時的幾個數(shù)據(jù)區(qū)域中惶我,程序計數(shù)器妈倔,虛擬機棧,本地方法棧3個區(qū)域隨著線程而生绸贡,隨線程而滅盯蝴,因此這幾個區(qū)域的內(nèi)存分配和回收具有確定性,不需要過多考慮垃圾回收問題恃轩,因為方法結(jié)束或者線程結(jié)束時结洼,內(nèi)存就回收了黎做。但是方法區(qū)和堆區(qū)不一樣叉跛,一個接口或者實現(xiàn)類所需要的內(nèi)存可能不一樣,一個方法的多個分支需要的內(nèi)存也可能不一樣蒸殿,只有程序運行時才能知道創(chuàng)建哪些對象筷厘,這部分內(nèi)存的分配和回收是動態(tài)的鸣峭。
在進行垃圾回收時候,首先需要判斷哪些對象需要回收酥艳,這就涉及到回收算法的問題摊溶。
一、垃圾回收算法
1.標記-清除算法
標記-清除算法是一種最基礎(chǔ)的垃圾收集算法充石,分為“標記”和“清除”兩步莫换。“標記”階段標記所有需要進行垃圾回收的對象骤铃,標記完成后統(tǒng)一回收被標記的對象。這種算法的不足點在于:
效率問題,標記和清除兩個過程效率都不高邑飒;
空間問題辜王,標記清除后會產(chǎn)生大量不連續(xù)碎片,后續(xù)如果需要為較大對象分配空間撕瞧,則又需觸發(fā)垃圾回收陵叽。
2.復(fù)制算法
為了解決標記-清除算法的效率問題,出現(xiàn)了復(fù)制算法丛版。這種算法把內(nèi)存按照容量劃分為大小相同的兩塊巩掺,每次只是用其中一塊,當這塊內(nèi)存用完了硼婿,就把還存活的對象復(fù)制到另外一塊中锌半,并將這塊的內(nèi)存清理掉,然后使用另外一塊寇漫,當另外一塊內(nèi)存用完了刊殉,再把存活的對象復(fù)制到這塊中,并清理另外一塊內(nèi)存州胳,依次類推记焊。
復(fù)制算法主要用于新生代的回收,在HotSpot虛擬機中栓撞,新生代內(nèi)存劃分為一塊較大的Eden空間遍膜,和兩塊較小的Survivor空間,每次使用Eden空間和其中一塊Survivor空間瓤湘。當進行垃圾回收時瓢颅,會把Eden空間和Survivor空間中存活的對象一次性復(fù)制到另外一塊Survivor空間上,最后清理掉Eden空間和剛才使用過的Survivor空間弛说。HotSpot虛擬機中挽懦,默認情況下Eden空間和Survivor空間的大小比例是8:1,即Eden空間占整個新生代的80%木人,每次新生代中使用的空間為80%+10%=90%信柿,閑置空間10%冀偶。
3.標記-整理算法
復(fù)制算法適用于那種對象存活率較低的場景,在對象存活率較高時渔嚷,使用復(fù)制收集算法意味著需要進行大量復(fù)制进鸠,會使效率降低,同時復(fù)制大量存活對象到另外一塊內(nèi)存形病,意味著需要有足夠大的內(nèi)存來保存這些對象客年,這勢必會降低內(nèi)存使用率。根據(jù)老年代的特點漠吻,有人提出標記-整理算法搀罢,和標記-清除算法不同的是,標記整理算法將存活的對象向一端移動侥猩,然后直接清理掉端邊界之外的內(nèi)存榔至。
4.分代收集算法
目前商業(yè)虛擬機中都使用分代收集算法。一般將Java堆分為新生代和老年代欺劳,新生代進行垃圾收集發(fā)現(xiàn)有大量對象死去唧取,只有少量對象存活,那么就使用復(fù)制算法划提。老年代中對象存活率較高枫弟,使用標記-清除算法或者標記-整理算法。
二鹏往、垃圾收集器
垃圾收集算法提供了內(nèi)存回收的方法論淡诗,垃圾收集器是內(nèi)存回收的方法論。每個廠商對垃圾收集器的實現(xiàn)不一樣伊履,這里主要討論Jdk1.7 Update 14之后的HotSpot虛擬機韩容。這個虛擬機中包含的垃圾收集器有如下7種:
以上收集器之間如果有連線,則表明可以搭配使用唐瀑,虛擬機所處區(qū)域群凶,表示他是新生代收集器還是老年代收集器。
1.Serial收集器
Serial收集器是一種最基本的單線程收集器哄辣,這種收集器工作時请梢,必須停止其他所有工作線程,優(yōu)點在于簡單高效力穗,但體驗很不友好毅弧,目前主要應(yīng)用場合是:虛擬機運行在Client模式下的默認新生代收集。器当窗。
2.ParNew收集器
parNew收集器是Serial收集器的多線程版本够坐,常用參數(shù)設(shè)置:
-XX:+UseConcMarkSweepGC
:設(shè)置ParNew為默認的新生代收集器;
-XX:+UseParNewGC
:指定使用ParNew為年輕代收集器,強制指定咆霜;
-XX:ParallelGCThreads=n
:設(shè)置收集器的線程數(shù)為n。
3.Parallel Scavenge收集器
Parallel Scavenge收集器是一個使用復(fù)制算法的新生代收集器嘶朱,這種收集器的主要目標是達到一個可控制的吞吐量(Throughput蛾坯,CPU用于運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間))疏遏。由于與吞吐量關(guān)系密切脉课,故而Parallel Scavenge收集器也稱為“吞吐量優(yōu)先”收集器。常用參數(shù)設(shè)置:
-XX:MaxGCPauseMillis=n
:設(shè)置年輕代垃圾收集的最長時間财异;
-XX:GCTimeRatio=n
:設(shè)置垃圾收集總可用時長的比例倘零,和吞吐量直接相關(guān);
-XX:+UseAdaptiveSizePolicy
:自適應(yīng)大小開關(guān)戳寸,配置該選項之后呈驶,每次GC后會重新計算 Eden、From 和 To 區(qū)的大小疫鹊,計算依據(jù)是 GC 過程中統(tǒng)計的 GC 時間袖瞻、吞吐量、內(nèi)存占用量拆吆,因此設(shè)置此參數(shù)之后就不需要再設(shè)置-XX:SurvivorRatio
聋迎、-XX:PretenureSizeThreshold
等參數(shù)了。
4.Serial Old收集器
Serial收集器的老年版本枣耀,也是一個單線程收集器霉晕,使用的是“標記-整理”算法,這種收集器的主要意義也是給Client模式下的虛擬機使用捞奕。
5.Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本牺堰,使用多線程和“標記-整理”算法。在注重吞吐量以及CPU資源敏感的場合颅围,都可以優(yōu)先考慮Parallel Scavenge收集器和Parallel Old收集器的組合萌焰。
6.CMS收集器
CMS收集器是一種以獲取最短回收停頓時間為目標的收集器。它基于“標記-清除”算法實現(xiàn)谷浅,運作過程相對于其他幾種收集器更復(fù)雜一些扒俯。分為以下四個過程:
初始標記(CMS initial mark):標記一下CG Roots能關(guān)聯(lián)到的對象;
并發(fā)標記(CMS concurrent mark):進行CG Roots Tracing的過程一疯;
重新標記(CMS remark):修正并發(fā)標記期間因用戶程序繼續(xù)運作而導(dǎo)致標記產(chǎn)生變動的那一部分對象的標記記錄撼玄。
并發(fā)清理(CMS concurrent sweep)
CMS 收集器的優(yōu)點在于并發(fā)收集,低停頓墩邀。其缺點在于以下三點:
CMS收集器對CPU很敏感掌猛,CMS默認回收線程是(CPU數(shù)量+3)/4,當CPU在4個以上時,并發(fā)收集時垃圾收集線程不少于25%的CPU資源荔茬,并隨著CPU數(shù)量增加而下降废膘。但是當CPU不足4個時,CMS對用戶程序的影響就會變得很大慕蔚。
CMS收集器無法處理浮動垃圾丐黄。由于CMS收集器并發(fā)清理階段用戶線程還在運行著,伴隨著程序運行就會有垃圾產(chǎn)生孔飒,這部分垃圾在標記過后灌闺,CMS收集器無法在當次收集中清理這些垃圾。
由于CMS收集器是一種基于“標記-清除”算法的收集器坏瞄,這種算法實現(xiàn)的收集器在收集結(jié)束后會有大量不連續(xù)碎片產(chǎn)生桂对。碎片過多時會給大對象分配帶來很大麻煩,往往老年代還有很大空間剩余鸠匀,但是無法找到連續(xù)空間分配當前對象蕉斜,因而不得不提前觸發(fā)Full GC。
7.G1收集器
G1收集器是一款面向服務(wù)端應(yīng)用的垃圾收集器缀棍,與其他收集器相比蛛勉,G1收集器具有如下優(yōu)點:
并發(fā)與并行:G1能充分利用多CPU,多核硬件優(yōu)勢睦柴,使用多個CPU來減少停頓時間诽凌;
分代收集:G1不需要其他收集器配合就能獨立管理整個堆的垃圾收集,且它能采用不同方式去處理新建對象和已經(jīng)存活了一段時間坦敌,熬過多次GC的舊對象以獲得更好的收集效果侣诵。
空間整合:使用G1收集器不會產(chǎn)生內(nèi)存碎片,收集后能提供規(guī)整的可用內(nèi)存狱窘。這種特性有利于程序長時間運行杜顺,分配大對象時候不會因為無法找到連續(xù)內(nèi)存空間而提前觸發(fā)下一次GC.
可預(yù)測的停頓:G1除了追求低停頓,還能建立可預(yù)測的停頓時間模型蘸炸,能讓使用著指定在長度為M毫秒的時間片段內(nèi)躬络,消耗在垃圾收集上的時間不超過N毫秒。
三搭儒、垃圾收集參數(shù)總結(jié)
參數(shù) | 描述 |
---|---|
UseSerialGC | 虛擬機運行在Client模式下的默認值穷当,打開此開關(guān)后,使用Serial+Serial Old的收集器組合進行內(nèi)存回收 |
UseParNewGC | 打開此開關(guān)后淹禾,使用ParNew + Serial Old 的收集器組合進行內(nèi)存回收 |
UseConcMarkSweepGC | 打開此開關(guān)后馁菜,使用ParNew + CMS + Serial Old 的收集器組合進行內(nèi)存回收。Serial Old 收集器將作為CMS收集器出現(xiàn)Concurrent Mode Failure失敗后的后備收集器使用 |
UseParallelGC | 虛擬機運行在Server 模式下的默認值铃岔,打開此開關(guān)后汪疮,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器組合進行內(nèi)存回收 |
UseParallelOldGC | 打開此開關(guān)后,使用Parallel Scavenge + Parallel Old 的收集器組合進行內(nèi)存回收 |
SurvivorRatio | 新生代中Eden 區(qū)域與Survivor 區(qū)域的容量比值,默認為8智嚷,代表Eden :Survivor=8∶1 |
PretenureSizeThreshold | 直接晉升到老年代的對象大小卖丸,設(shè)置這個參數(shù)后,大于這個參數(shù)的對象將直接在老年代分配 |
MaxTenuringThreshold | 晉升到老年代的對象年齡盏道。每個對象在堅持過一次Minor GC 之后稍浆,年齡就加1,當超過這個參數(shù)值時就進入老年代 |
UseAdaptiveSizePolicy | 動態(tài)調(diào)整Java 堆中各個區(qū)域的大小以及進入老年代的年齡 |
HandlePromotionFailure | 是否允許分配擔保失敗摇天,即老年代的剩余空間不足以應(yīng)付新生代的整個Eden 和Survivor 區(qū)的所有對象都存活的極端情況 |
ParallelGCThreads | 設(shè)置并行GC 時進行內(nèi)存回收的線程數(shù) |
GCTimeRatio | GC 時間占總時間的比率,默認值為99恐仑,即允許1% 的GC 時間泉坐。僅在使用Parallel Scavenge 收集器時生效 |
MaxGCPauseMillis | 設(shè)置GC 的最大停頓時間。僅在使用Parallel Scavenge 收集器時生效 |
CMSInitiatingOccupancyFraction | 設(shè)置CMS 收集器在老年代空間被使用多少后觸發(fā)垃圾收集裳仆。默認值為68%腕让,僅在使用CMS 收集器時生效 |
UseCMSCompactAtFullCollection | 設(shè)置CMS 收集器在完成垃圾收集后是否要進行一次內(nèi)存碎片整理。僅在使用CMS 收集器時生效 |
CMSFullGCsBeforeCompaction | 設(shè)置CMS 收集器在進行若干次垃圾收集后再啟動一次內(nèi)存碎片整理歧斟,僅在使用CMS 收集器時生效 |