上文緊接此處
OutOfMemoryError 異常(OOM)
Java堆溢出
Java堆用于存儲(chǔ)對(duì)象實(shí)例刨啸,只要不斷的創(chuàng)建對(duì)象芋膘,并且保證GCRoots到對(duì)象之間有可達(dá)路徑來(lái)避免垃圾回收機(jī)制清除這些對(duì)象懂拾,那么在數(shù)量到達(dá)最大堆的容量限制后就會(huì)產(chǎn)生內(nèi)存溢出異常
如果是內(nèi)存泄漏,可進(jìn)一步通過(guò)工具查看泄漏對(duì)象到GC Roots的引用鏈逃默。就能找到泄露對(duì)象是通過(guò)怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無(wú)法自動(dòng)回收它們的攀圈。掌握了泄漏對(duì)象的類(lèi)型信息及GC Roots引用鏈的信息昭娩,就可以比較準(zhǔn)確地定位出泄漏代碼的位置
如果不存在泄露,換句話說(shuō)黍匾,就是內(nèi)存中的對(duì)象確實(shí)都還必須存活著栏渺,那就應(yīng)當(dāng)檢查虛擬機(jī)的堆參數(shù)(-Xmx與-Xms),與機(jī)器物理內(nèi)存對(duì)比看是否還可以調(diào)大锐涯,從代碼上檢查是否存在某些對(duì)象生命周期過(guò)長(zhǎng)磕诊、持有狀態(tài)時(shí)間過(guò)長(zhǎng)的情況,嘗試減少程序運(yùn)行期的內(nèi)存消耗
虛擬機(jī)棧和本地方法棧溢出
對(duì)于HotSpot來(lái)說(shuō)纹腌,雖然-Xoss參數(shù)(設(shè)置本地方法棧大婿铡)存在,但實(shí)際上是無(wú)效的升薯,棧容量只由-Xss參數(shù)設(shè)定莱褒。關(guān)于虛擬機(jī)棧和本地方法棧,在Java虛擬機(jī)規(guī)范中描述了兩種異常:
如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的最大深度涎劈,將拋出StackOverflowError
如果虛擬機(jī)在擴(kuò)展棧時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存空間保礼,則拋出OutOfMemoryError異常
在單線程下,無(wú)論由于棧幀太大還是虛擬機(jī)棧容量太小责语,當(dāng)內(nèi)存無(wú)法分配的時(shí)候炮障,虛擬機(jī)拋出的都是StackOverflowError異常
如果是多線程導(dǎo)致的內(nèi)存溢出,與椑ず颍空間是否足夠大并不存在任何聯(lián)系胁赢,這個(gè)時(shí)候每個(gè)線程的棧分配的內(nèi)存越大纹磺,反而越容易產(chǎn)生內(nèi)存溢出異常净赴。解決的時(shí)候是在不能減少線程數(shù)或更換64為的虛擬機(jī)的情況下宰啦,就只能通過(guò)減少最大堆和減少棧容量來(lái)?yè)Q取更多的線程
方法區(qū)和運(yùn)行時(shí)常量池溢出
String.intern()是一個(gè)Native方法又固,它的作用是:如果字符串常量池中已經(jīng)包含一個(gè)等于此String對(duì)象的字符串另绩,則返回代表池中這個(gè)字符串的String對(duì)象娇妓;否則刑巧,將此String對(duì)象包含的字符串添加到常量池中搂蜓,并且返回此String對(duì)象的引用
由于常量池分配在永久代中顽照,可以通過(guò)-XX:PermSize和-XX:MaxPermSize限制方法區(qū)大小由蘑,從而間接限制其中常量池的容量。
Intern():
JDK1.6 intern方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代代兵,返回的也是永久代中這個(gè)字符串實(shí)例的引用尼酿,而由StringBuilder創(chuàng)建的字符串實(shí)例在Java堆上,所以必然不是一個(gè)引用
JDK1.7 intern()方法的實(shí)現(xiàn)不會(huì)再?gòu)?fù)制實(shí)例植影,只是在常量池中記錄首次出現(xiàn)的實(shí)例引用裳擎,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例是同一個(gè)
JVM垃圾回收(GC)
? 程序計(jì)數(shù)器、虛擬機(jī)棧思币、本地方法棧3個(gè)區(qū)域隨線程而生鹿响,隨線程而滅羡微,在這幾個(gè)區(qū)域內(nèi)就不需要過(guò)多考慮回收的問(wèn)題,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí)惶我,內(nèi)存自然就跟隨著回收拷淘。
棧中的棧幀隨著方法的進(jìn)入和退出就有條不紊的執(zhí)行者出棧和入棧的操作,每一個(gè)棧分配多少個(gè)內(nèi)存基本都是在類(lèi)結(jié)構(gòu)確定下來(lái)的時(shí)候就已經(jīng)確定了指孤,這幾個(gè)區(qū)域內(nèi)存分配和回收都具有確定性启涯。
而堆和方法區(qū)則不同,一個(gè)接口的實(shí)現(xiàn)是多種多樣的恃轩,多個(gè)實(shí)現(xiàn)類(lèi)需要的內(nèi)存可能不一樣结洼,一個(gè)方法中多個(gè)分支需要的內(nèi)存也不一樣,我們只能在程序運(yùn)行的期間知道需要?jiǎng)?chuàng)建那些對(duì)象叉跛,分配多少內(nèi)存松忍,這部分的內(nèi)存分配和回收都是動(dòng)態(tài)的。
GC的基本原理:將內(nèi)存中不再被引用的對(duì)象進(jìn)行回收筷厘,GC中用于回收的方法稱(chēng)為收集器鸣峭。垃圾:不再被引用的對(duì)象。
由于GC需要消耗一些資源和時(shí)間酥艳,Java在對(duì)對(duì)象的生命周期特征進(jìn)行分析后摊溶,按照新生代、舊生代的方式來(lái)對(duì)對(duì)象進(jìn)行收集充石,以盡可能的縮短GC對(duì)應(yīng)用造成的暫停莫换。
- 對(duì)新生代的對(duì)象的收集稱(chēng)為minor GC拉岁;
- 對(duì)舊生代的對(duì)象的收集稱(chēng)為Full GC;
- 程序中主動(dòng)調(diào)用System.gc()的GC為Full GC喊暖。
Java垃圾回收是單獨(dú)的后臺(tái)線程gc執(zhí)行的撕瞧,自動(dòng)運(yùn)行無(wú)需顯示調(diào)用。即使主動(dòng)調(diào)用了java.lang.System.gc()咨跌,該方法也只會(huì)提醒系統(tǒng)進(jìn)行垃圾回收硼婿,但系統(tǒng)不一定會(huì)回應(yīng)禽车,可能會(huì)不予理睬刊殉。
哪些內(nèi)存需要回收记焊?
1,引用計(jì)數(shù)算法
算法分析
引用計(jì)數(shù)是垃圾收集器中的早期策略。在這種方法中栓撞,堆中每個(gè)對(duì)象實(shí)例都有一個(gè)引用計(jì)數(shù)遍膜。當(dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),就將該對(duì)象實(shí)例分配給一個(gè)變量瓤湘,該變量計(jì)數(shù)設(shè)置為1瓢颅。當(dāng)任何其它變量被賦值為這個(gè)對(duì)象的引用時(shí),計(jì)數(shù)加1(a = b,則b引用的對(duì)象實(shí)例的計(jì)數(shù)器+1)弛说,但當(dāng)一個(gè)對(duì)象實(shí)例的某個(gè)引用超過(guò)了生命周期或者被設(shè)置為一個(gè)新值時(shí)挽懦,對(duì)象實(shí)例的引用計(jì)數(shù)器減1。任何引用計(jì)數(shù)器為0的對(duì)象實(shí)例可以被當(dāng)作垃圾收集木人。當(dāng)一個(gè)對(duì)象實(shí)例被垃圾收集時(shí)信柿,它引用的任何對(duì)象實(shí)例的引用計(jì)數(shù)器減1。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):引用計(jì)數(shù)收集器可以很快的執(zhí)行醒第,交織在程序運(yùn)行中渔嚷。對(duì)程序需要不被長(zhǎng)時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利。
缺點(diǎn):無(wú)法檢測(cè)出循環(huán)引用稠曼。如父對(duì)象有一個(gè)對(duì)子對(duì)象的引用圃伶,子對(duì)象反過(guò)來(lái)引用父對(duì)象。這樣蒲列,他們的引用計(jì)數(shù)永遠(yuǎn)不可能為0窒朋。
public class ReferenceFindTest {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
}
這段代碼是用來(lái)驗(yàn)證引用計(jì)數(shù)算法不能檢測(cè)出循環(huán)引用。最后面兩句將object1
和object2
賦值為null
蝗岖,也就是說(shuō)object1
和object2
指向的對(duì)象已經(jīng)不可能再被訪問(wèn)侥猩,但是由于它們互相引用對(duì)方,導(dǎo)致它們的引用計(jì)數(shù)器都不為0抵赢,那么垃圾收集器就永遠(yuǎn)不會(huì)回收它們。
2,可達(dá)性分析算法
可達(dá)性分析算法是從離散數(shù)學(xué)中的圖論引入的划提,程序把所有的引用關(guān)系看作一張圖鹏往,從一個(gè)節(jié)點(diǎn)GC ROOT開(kāi)始伊履,尋找對(duì)應(yīng)的引用節(jié)點(diǎn),找到這個(gè)節(jié)點(diǎn)以后群凶,繼續(xù)尋找這個(gè)節(jié)點(diǎn)的引用節(jié)點(diǎn)请梢,當(dāng)所有的引用節(jié)點(diǎn)尋找完畢之后,剩余的節(jié)點(diǎn)則被認(rèn)為是沒(méi)有被引用到的節(jié)點(diǎn)睛廊,即無(wú)用的節(jié)點(diǎn)咆霜,無(wú)用的節(jié)點(diǎn)將會(huì)被判定為是可回收的對(duì)象蛾坯。
在Java中脉课,可作為GC Root的對(duì)象包括以下幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
- 方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
- 本地方法棧中JNI(即一般說(shuō)的Native方法)引用的對(duì)象
Java中的引用你了解多少
在Java語(yǔ)言中倘零,將引用又分為強(qiáng)引用呈驶、軟引用袖瞻、弱引用聋迎、虛引用4種霉晕,這四種引用強(qiáng)度依次逐漸減弱娄昆。
-
1扒俯,強(qiáng)引用
在程序代碼中普遍存在的撼玄,類(lèi)似
Object obj = new Object()
這類(lèi)引用掌猛,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象竹海。 2孔飒,軟引用
用來(lái)描述一些還有用但并非必須的對(duì)象坏瞄。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象鸠匀,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前狮崩,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收睦柴。如果這次回收后還沒(méi)有足夠的內(nèi)存坦敌,才會(huì)拋出內(nèi)存溢出異常杜顺。
- 3躬络,弱引用
也是用來(lái)描述非必需對(duì)象的穷当,但是它的強(qiáng)度比軟引用更弱一些馁菜,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí)毁习,無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象纤勒。
- 4摇天,虛引用
也叫幽靈引用或幻影引用(名字真會(huì)取,很魔幻的樣子)裳仆,是最弱的一種引用關(guān)系纯丸。一個(gè)對(duì)象是否有虛引用的存在觉鼻,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響坠陈,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例仇矾。它的作用是能在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知贮匕。
不要被概念嚇到粗合,也別擔(dān)心隙疚,還沒(méi)跑題供屉,再深入伶丐,可就不好說(shuō)了。這四個(gè)概念的目的是為了說(shuō)明漓雅,無(wú)論引用計(jì)數(shù)算法還是可達(dá)性分析算法都是基于強(qiáng)引用而言的组题。
對(duì)象死亡(被回收)前的最后一次掙扎
即使在可達(dá)性分析算法中不可達(dá)的對(duì)象崔列,也并非是“非死不可”赵讯,這時(shí)候它們暫時(shí)處于“緩刑”階段边翼,要真正宣告一個(gè)對(duì)象死亡讯私,至少要經(jīng)歷兩次標(biāo)記過(guò)程斤寇。
第一次標(biāo)記:如果對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與GC Roots相連接的引用鏈牙寞,那它將會(huì)被第一次標(biāo)記间雀;
第二次標(biāo)記:第一次標(biāo)記后接著會(huì)進(jìn)行一次篩選惹挟,篩選的條件是此對(duì)象是否有必要執(zhí)行finalize()
方法连锯。在finalize()
方法中沒(méi)有重新與引用鏈建立關(guān)聯(lián)關(guān)系的,將被進(jìn)行第二次標(biāo)記摇展。
第二次標(biāo)記成功的對(duì)象將真的會(huì)被回收咏连,如果對(duì)象在finalize()
方法中重新與引用鏈建立了關(guān)聯(lián)關(guān)系捻勉,那么將會(huì)逃離本次回收踱启,繼續(xù)存活。猿們還跟的上吧榜晦,嘿嘿乾胶。
方法區(qū)如何判斷是否需要回收
方法區(qū)主要回收的內(nèi)容有:廢棄常量和無(wú)用的類(lèi)。對(duì)于廢棄常量也可通過(guò)引用的可達(dá)性來(lái)判斷脑融,但是對(duì)于無(wú)用的類(lèi)則需要同時(shí)滿(mǎn)足下面3個(gè)條件:
該類(lèi)所有的實(shí)例都已經(jīng)被回收肘迎,也就是Java堆中不存在該類(lèi)的任何實(shí)例妓布;
加載該類(lèi)的
ClassLoader
已經(jīng)被回收匣沼;-
該類(lèi)對(duì)應(yīng)的
java.lang.Class
對(duì)象沒(méi)有在任何地方被引用肛著,無(wú)法在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法。
常用的垃圾收集算法Java常用的回收算法
1刀脏,標(biāo)記-清除算法
標(biāo)記-清除算法采用從根集合(GC Roots)進(jìn)行掃描,對(duì)存活的對(duì)象進(jìn)行標(biāo)記轮傍,標(biāo)記完畢后创夜,再掃描整個(gè)空間中未被標(biāo)記的對(duì)象涧尿,進(jìn)行回收檬贰,如下圖所示桥言。標(biāo)記-清除算法不需要進(jìn)行對(duì)象的移動(dòng)号阿,只需對(duì)不存活的對(duì)象進(jìn)行處理倦西,在存活對(duì)象比較多的情況下極為高效扰柠,但由于標(biāo)記-清除算法直接回收不存活的對(duì)象卤档,因此會(huì)造成內(nèi)存碎片汤踏。
2,復(fù)制算法
復(fù)制算法的提出是為了克服句柄的開(kāi)銷(xiāo)和解決內(nèi)存碎片的問(wèn)題稳诚。它開(kāi)始時(shí)把堆分成 一個(gè)對(duì)象 面和多個(gè)空閑面扳还, 程序從對(duì)象面為對(duì)象分配空間氨距,當(dāng)對(duì)象滿(mǎn)了楞遏,基于copying算法的垃圾 收集就從根集合(GC Roots)中掃描活動(dòng)對(duì)象橱健,并將每個(gè) 活動(dòng)對(duì)象復(fù)制到空閑面(使得活動(dòng)對(duì)象所占的內(nèi)存之間沒(méi)有空閑洞)拘荡,這樣空閑面變成了對(duì)象面珊皿,原來(lái)的對(duì)象面變成了空閑面蟋定,程序會(huì)在新的對(duì)象面中分配內(nèi)存驶兜。
3屠凶,標(biāo)記-整理算法
標(biāo)記-整理算法采用標(biāo)記-清除算法一樣的方式進(jìn)行對(duì)象的標(biāo)記矗愧,但在清除時(shí)不同,在回收不存活的對(duì)象占用的空間后属愤,會(huì)將所有的存活對(duì)象往左端空閑空間移動(dòng)春塌,并更新對(duì)應(yīng)的指針。標(biāo)記-整理算法是在標(biāo)記-清除算法的基礎(chǔ)上,又進(jìn)行了對(duì)象的移動(dòng)事格,因此成本更高驹愚,但是卻解決了內(nèi)存碎片的問(wèn)題逢捺。具體流程見(jiàn)下圖:
4倘潜,分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法涮因。它的核心思想是根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域养泡。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區(qū)之外還有一個(gè)代就是永久代(Permanet Generation)输硝。老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收点把,而新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量的對(duì)象需要被回收,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法褒翰。
5,年輕代(Young Generation)的回收算法
a) 所有新生成的對(duì)象首先都是放在年輕代的揣非。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對(duì)象。
b) 新生代內(nèi)存按照8:1:1的比例分為一個(gè)eden區(qū)和兩個(gè)survivor(survivor0,survivor1)區(qū)搞监。一個(gè)Eden區(qū)琐驴,兩個(gè) Survivor區(qū)(一般而言)安疗。大部分對(duì)象在Eden區(qū)中生成∮窆蓿回收時(shí)先將eden區(qū)存活對(duì)象復(fù)制到一個(gè)survivor0區(qū),然后清空eden區(qū)季蚂,當(dāng)這個(gè)survivor0區(qū)也存放滿(mǎn)了時(shí)扭屁,則將eden區(qū)和survivor0區(qū)存活對(duì)象復(fù)制到另一個(gè)survivor1區(qū),然后清空eden和這個(gè)survivor0區(qū)涩禀,此時(shí)survivor0區(qū)是空的料滥,然后將survivor0區(qū)和survivor1區(qū)交換,即保持survivor1區(qū)為空艾船, 如此往復(fù)葵腹。
c) 當(dāng)survivor1區(qū)不足以存放 eden和survivor0的存活對(duì)象時(shí),就將存活對(duì)象直接存放到老年代屿岂。若是老年代也滿(mǎn)了就會(huì)觸發(fā)一次Full GC践宴,也就是新生代、老年代都進(jìn)行回收徒坡。
d) 新生代發(fā)生的GC也叫做Minor GC锦溪,MinorGC發(fā)生頻率比較高(不一定等Eden區(qū)滿(mǎn)了才觸發(fā))。
6,年老代(Old Generation)的回收算法
a) 在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象档礁,就會(huì)被放到年老代中易迹。因此窘疮,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象蔚出。
b) 內(nèi)存比新生代也大很多(大概比例是1:2)趋翻,當(dāng)老年代內(nèi)存滿(mǎn)時(shí)觸發(fā)Major GC即Full GC,F(xiàn)ull GC發(fā)生頻率比較低,老年代對(duì)象存活時(shí)間比較長(zhǎng),存活率標(biāo)記高。
7,持久代(Permanent Generation)的回收算法
用于存放靜態(tài)文件醉顽,如Java類(lèi)唆涝、方法等晓猛。持久代對(duì)垃圾回收沒(méi)有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Hibernate 等客情,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來(lái)存放這些運(yùn)行過(guò)程中新增的類(lèi)仰担。持久代也稱(chēng)方法區(qū)拌滋,具體的回收可參見(jiàn)上文祭隔。
常見(jiàn)的垃圾收集器
JVM GC收集器的回顧與比較
圖中展示了7種不同分代的收集器:
Serial、ParNew、Parallel Scavenge融师、Serial Old脆烟、Parallel Old洼怔、CMS轻猖、G1;
而它們所處區(qū)域醋寝,則表明其是屬于新生代收集器還是老年代收集器:
新生代收集器:Serial办陷、ParNew鲸鹦、Parallel Scavenge;
老年代收集器:Serial Old、Parallel Old、CMS仇冯;
整堆收集器:G1娇昙;
兩個(gè)收集器間有連線薄嫡,表明它們可以搭配使用:
Serial/Serial Old、Serial/CMS、ParNew/Serial Old蘸鲸、ParNew/CMS、Parallel Scavenge/Serial Old挪哄、Parallel Scavenge/Parallel Old、G1帝美;
Serial收集器(復(fù)制算法)
新生代單線程收集器漆枚,標(biāo)記和清理都是單線程,優(yōu)點(diǎn)是簡(jiǎn)單高效恼布。是client級(jí)別默認(rèn)的GC方式,可以通過(guò)-XX:+UseSerialGC
來(lái)強(qiáng)制指定。Serial Old收集器(標(biāo)記-整理算法)
老年代單線程收集器而咆,Serial收集器的老年代版本疮薇。
串行收集器采用單線程stop-the-world的方式進(jìn)行收集胸墙。當(dāng)內(nèi)存不足時(shí),串行GC設(shè)置停頓標(biāo)識(shí)按咒,待所有線程都進(jìn)入安全點(diǎn)(Safepoint)時(shí)迟隅,應(yīng)用線程暫停,串行GC開(kāi)始工作励七,采用單線程方式回收空間并整理內(nèi)存玻淑。單線程也意味著復(fù)雜度更低、占用內(nèi)存更少呀伙,但同時(shí)也意味著不能有效利用多核優(yōu)勢(shì)。事實(shí)上添坊,串行收集器特別適合堆內(nèi)存不高剿另、單核甚至雙核CPU的場(chǎng)合。
Parallel Scavenge收集器(停止-復(fù)制算法)
并行收集器贬蛙,追求高吞吐量,高效利用CPU氛堕。吞吐量一般為99%讼稚, 吞吐量= 用戶(hù)線程時(shí)間/(用戶(hù)線程時(shí)間+GC線程時(shí)間)。適合后臺(tái)應(yīng)用等對(duì)交互相應(yīng)要求不高的場(chǎng)景乍狐。是server級(jí)別默認(rèn)采用的GC方式藕帜,可用-XX:+UseParallelGC
來(lái)強(qiáng)制指定洽故,用-XX:ParallelGCThreads=4
來(lái)指定線程數(shù)饿这。Parallel Old收集器(停止-復(fù)制算法)
Parallel Scavenge收集器的老年代版本长捧,并行收集器串结,吞吐量?jī)?yōu)先。
并行收集器組合 Parallel Scavenge + Parallel Old
開(kāi)啟選項(xiàng):-XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)
并行收集器是以關(guān)注吞吐量為目標(biāo)的垃圾收集器把敞,也是server模式下的默認(rèn)收集器配置奋早,對(duì)吞吐量的關(guān)注主要體現(xiàn)在年輕代Parallel Scavenge收集器上。
并行收集器與串行收集器工作模式相似期揪,都是stop-the-world方式姓建,只是暫停時(shí)并行地進(jìn)行垃圾收集引瀑。年輕代采用復(fù)制算法,老年代采用標(biāo)記-整理屑柔,在回收的同時(shí)還會(huì)對(duì)內(nèi)存進(jìn)行壓縮死陆。關(guān)注吞吐量主要指年輕代的Parallel Scavenge收集器措译,通過(guò)兩個(gè)目標(biāo)參數(shù)
-XX:MaxGCPauseMills和-XX:GCTimeRatio
领虹,調(diào)整新生代空間大小,來(lái)降低GC觸發(fā)的頻率最疆。
ParNew收集器(停止-復(fù)制算法)
新生代收集器蚤告,可以認(rèn)為是Serial收集器的多線程版本,在多核CPU環(huán)境下有著比Serial更好的表現(xiàn)蚊逢。-
CMS(Concurrent Mark Sweep)收集器(標(biāo)記-清理算法)
高并發(fā)、低停頓檬寂,追求最短GC回收停頓時(shí)間,cpu占用比較高镣屹,響應(yīng)時(shí)間快,停頓時(shí)間短,多核cpu 追求高響應(yīng)時(shí)間的選擇逸寓。此處經(jīng)典圖解 CMS 垃圾回收機(jī)制原理并發(fā)標(biāo)記清除收集器組合
并發(fā)標(biāo)記清除收集器組合 ParNew + CMS + Serial Old
開(kāi)啟選項(xiàng):-XX:+UseConcMarkSweepGC
并發(fā)標(biāo)記清除(CMS)是以關(guān)注延遲為目標(biāo)簇宽、十分優(yōu)秀的垃圾回收算法譬嚣,開(kāi)啟后,年輕代使用STW式的并行收集盐股,老年代回收采用CMS進(jìn)行垃圾回收疯汁,對(duì)延遲的關(guān)注也主要體現(xiàn)在老年代CMS上。
年輕代ParNew與并行收集器類(lèi)似溢豆,而老年代CMS每個(gè)收集周期都要經(jīng)歷:初始標(biāo)記漩仙、并發(fā)標(biāo)記队他、重新標(biāo)記麸折、并發(fā)清除窜锯。其中衬浑,初始標(biāo)記以STW的方式標(biāo)記所有的根對(duì)象;并發(fā)標(biāo)記則同應(yīng)用線程一起并行放刨,標(biāo)記出根對(duì)象的可達(dá)路徑工秩;在進(jìn)行垃圾回收前,CMS再以一個(gè)STW進(jìn)行重新標(biāo)記进统,標(biāo)記那些由mutator線程(指引起數(shù)據(jù)變化的線程助币,即應(yīng)用線程)修改而可能錯(cuò)過(guò)的可達(dá)對(duì)象;最后得到的不可達(dá)對(duì)象將在并發(fā)清除階段進(jìn)行回收螟碎。值得注意的是眉菱,初始標(biāo)記和重新標(biāo)記都已優(yōu)化為多線程執(zhí)行酥郭。CMS非常適合堆內(nèi)存大椿息、CPU核數(shù)多的服務(wù)器端應(yīng)用孟抗,也是G1出現(xiàn)之前大型應(yīng)用的首選收集器亿胸。
但是CMS并不完美突颊,它有以下缺點(diǎn):
1船惨,由于并發(fā)進(jìn)行,CMS在收集與應(yīng)用線程會(huì)同時(shí)會(huì)增加對(duì)堆內(nèi)存的占用舱殿,也就是說(shuō)冈绊,CMS必須要在老年代堆內(nèi)存用盡之前完成垃圾回收,否則CMS回收失敗時(shí),將觸發(fā)擔(dān)保機(jī)制,串行老年代收集器將會(huì)以STW的方式進(jìn)行一次GC施绎,從而造成較大停頓時(shí)間;
2刃永,標(biāo)記清除算法無(wú)法整理空間碎片,老年代空間會(huì)隨著應(yīng)用時(shí)長(zhǎng)被逐步耗盡,最后將不得不通過(guò)擔(dān)保機(jī)制對(duì)堆內(nèi)存進(jìn)行壓縮。CMS也提供了參數(shù)-XX:CMSFullGCsBeForeCompaction
(默認(rèn)0祝沸,即每次都進(jìn)行內(nèi)存整理)來(lái)指定多少次CMS收集之后,進(jìn)行一次壓縮的Full GC。
Garbage First
Garbage First (G1)G1垃圾收集器詳解
開(kāi)啟選項(xiàng):
-XX:+UseG1GC
之前介紹的幾組垃圾收集器組合岔绸,都有幾個(gè)共同點(diǎn):
1谴分,年輕代舀奶、老年代是獨(dú)立且連續(xù)的內(nèi)存塊;
2斋射,年輕代收集使用單eden育勺、雙survivor進(jìn)行復(fù)制算法;
3罗岖,老年代收集必須掃描整個(gè)老年代區(qū)域涧至;
4,都是以盡可能少而塊地執(zhí)行GC為設(shè)計(jì)原則桑包。
G1垃圾收集器也是以關(guān)注延遲為目標(biāo)南蓬、服務(wù)器端應(yīng)用的垃圾收集器,被HotSpot團(tuán)隊(duì)寄予取代CMS的使命哑了,也是一個(gè)非常具有調(diào)優(yōu)潛力的垃圾收集器蓖康。雖然G1也有類(lèi)似CMS的收集動(dòng)作:初始標(biāo)記、并發(fā)標(biāo)記垒手、重新標(biāo)記蒜焊、清除、轉(zhuǎn)移回收科贬,并且也以一個(gè)串行收集器做擔(dān)保機(jī)制泳梆,但單純地以類(lèi)似前三種的過(guò)程描述顯得并不是很妥當(dāng)。事實(shí)上榜掌,G1收集與以上三組收集器有很大不同:
- 1优妙,G1的設(shè)計(jì)原則是"首先收集盡可能多的垃圾(Garbage First)"。因此憎账,G1并不會(huì)等內(nèi)存耗盡(串行套硼、并行)或者快耗盡(CMS)的時(shí)候開(kāi)始垃圾收集,而是在內(nèi)部采用了啟發(fā)式算法胞皱,在老年代找出具有高收集收益的分區(qū)進(jìn)行收集邪意。同時(shí)G1可以根據(jù)用戶(hù)設(shè)置的暫停時(shí)間目標(biāo)自動(dòng)調(diào)整年輕代和總堆大小,暫停目標(biāo)越短年輕代空間越小反砌、總空間就越大雾鬼;
- 2,G1采用內(nèi)存分區(qū)(Region)的思路宴树,將內(nèi)存劃分為一個(gè)個(gè)相等大小的內(nèi)存分區(qū)策菜,回收時(shí)則以分區(qū)為單位進(jìn)行回收,存活的對(duì)象復(fù)制到另一個(gè)空閑分區(qū)中。由于都是以相等大小的分區(qū)為單位進(jìn)行操作又憨,因此G1天然就是一種壓縮方案(局部壓縮)翠霍;
- 3,G1雖然也是分代收集器蠢莺,但整個(gè)內(nèi)存分區(qū)不存在物理上的年輕代與老年代的區(qū)別壶运,也不需要完全獨(dú)立的survivor(to space)堆做復(fù)制準(zhǔn)備。G1只有邏輯上的分代概念浪秘,或者說(shuō)每個(gè)分區(qū)都可能隨G1的運(yùn)行在不同代之間前后切換;
- 4埠况,G1的收集都是STW的耸携,但年輕代和老年代的收集界限比較模糊,采用了混合(mixed)收集的方式辕翰。即每次收集既可能只收集年輕代分區(qū)(年輕代收集)夺衍,也可能在收集年輕代的同時(shí),包含部分老年代分區(qū)(混合收集)喜命,這樣即使堆內(nèi)存很大時(shí)沟沙,也可以限制收集范圍,從而降低停頓壁榕。
CMS與G1比較
CMS收集器是基于標(biāo)記清除算法實(shí)現(xiàn)的矛紫,整個(gè)過(guò)程分為4個(gè)步驟:
1.初始標(biāo)記2.并發(fā)標(biāo)記3.重新標(biāo)記4.并發(fā)清除
優(yōu)點(diǎn):并發(fā)收集、低停頓
缺點(diǎn):
1.CMS收集器對(duì)CPU資源非常敏感牌里,CMS默認(rèn)啟動(dòng)的回收線程數(shù)是(CPU數(shù)量+3)/4颊咬,
2.CMS收集器無(wú)法處理浮動(dòng)垃圾,可能出現(xiàn)Failure失敗而導(dǎo)致一次Full G場(chǎng)地產(chǎn)生
3.CMS是基于標(biāo)記清除算法實(shí)現(xiàn)的
G1收集器:
它是一款面向服務(wù)器應(yīng)用的垃圾收集器
1.并行與并發(fā):利用多CPU縮短STOP-The-World停頓的時(shí)間
2.分代收集
3.空間整合:不會(huì)產(chǎn)生內(nèi)存碎片
4.可預(yù)測(cè)的停頓
運(yùn)作方式:初始標(biāo)記牡辽,并發(fā)標(biāo)記喳篇,最終標(biāo)記,篩選回收
GC是什么時(shí)候觸發(fā)的 态辛?
由于對(duì)象進(jìn)行了分代處理麸澜,因此垃圾回收區(qū)域、時(shí)間也不一樣奏黑。GC有兩種類(lèi)型:Scavenge GC和Full GC炊邦。
Scavenge GC
一般情況下,當(dāng)新對(duì)象生成熟史,并且在Eden申請(qǐng)空間失敗時(shí)铣耘,就會(huì)觸發(fā)Scavenge GC,對(duì)Eden區(qū)域進(jìn)行GC以故,清除非存活對(duì)象蜗细,并且把尚且存活的對(duì)象移動(dòng)到Survivor區(qū)。然后整理Survivor的兩個(gè)區(qū)。這種方式的GC是對(duì)年輕代的Eden區(qū)進(jìn)行炉媒,不會(huì)影響到年老代踪区。因?yàn)榇蟛糠謱?duì)象都是從Eden區(qū)開(kāi)始的,同時(shí)Eden區(qū)不會(huì)分配的很大吊骤,所以Eden區(qū)的GC會(huì)頻繁進(jìn)行缎岗。因而,一般在這里需要使用速度快白粉、效率高的算法传泊,使Eden去能盡快空閑出來(lái)。
Full GC
對(duì)整個(gè)堆進(jìn)行整理鸭巴,包括Young眷细、Tenured和Perm。Full GC因?yàn)樾枰獙?duì)整個(gè)堆的對(duì)老年代/永久代進(jìn)行回收鹃祖,所以比Scavenge GC要慢溪椎,因此應(yīng)該盡可能減少Full GC的次數(shù)。在對(duì)JVM調(diào)優(yōu)的過(guò)程中恬口,很大一部分工作就是對(duì)于Full GC的調(diào)節(jié)校读。有如下原因可能導(dǎo)致Full GC:
a) 年老代(Tenured)被寫(xiě)滿(mǎn);
b) 持久代(Perm)被寫(xiě)滿(mǎn)祖能;
c) System.gc()被顯示調(diào)用歉秫;
d) 上一次GC之后Heap的各域分配策略動(dòng)態(tài)變化;
最后再次強(qiáng)調(diào)一下結(jié)論:
Full GC == Major GC指的是對(duì)老年代/永久代的stop the world的GC
Full GC的次數(shù) = 老年代GC時(shí) stop the world的次數(shù)
Full GC的時(shí)間 = 老年代GC時(shí) stop the world的總時(shí)間
CMS 不等于Full GC养铸,我們可以看到CMS分為多個(gè)階段端考,只有stop the world的階段被計(jì)算到了Full GC的次數(shù)和時(shí)間,而和業(yè)務(wù)線程并發(fā)的GC的次數(shù)和時(shí)間則不被認(rèn)為是Full GC
Full GC本身不會(huì)先進(jìn)行Minor GC揭厚,我們可以配置却特,讓Full GC之前先進(jìn)行一次Minor GC,因?yàn)槔夏甏芏鄬?duì)象都會(huì)引用到新生代的對(duì)象筛圆,先進(jìn)行一次Minor GC可以提高老年代GC的速度裂明。比如老年代使用CMS時(shí),設(shè)置CMSScavengeBeforeRemark優(yōu)化太援,讓CMS remark之前先進(jìn)行一次Minor GC闽晦。
文章參考:
深入理解Java虛擬機(jī)---學(xué)習(xí)感悟以及筆記