兩篇文章讓你徹底理解Java虛擬機(jī)(二)

上文緊接此處

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)引用。最后面兩句將object1object2賦值為null蝗岖,也就是說(shuō)object1object2指向的對(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ì)象蛾坯。

可達(dá)性分析算法.png

在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)存碎片汤踏。

image

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)存驶兜。

image

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)下圖:

image

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)采取最適合的收集算法褒翰。

image

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收集器的回顧與比較

image

圖中展示了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收集器的老年代版本疮薇。

image

串行收集器采用單線程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)先。

image

并行收集器組合 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ī)制原理

    image

    并發(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

image

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é)論:

  1. Full GC == Major GC指的是對(duì)老年代/永久代的stop the world的GC

  2. Full GC的次數(shù) = 老年代GC時(shí) stop the world的次數(shù)

  3. Full GC的時(shí)間 = 老年代GC時(shí) stop the world的總時(shí)間

  4. CMS 不等于Full GC养铸,我們可以看到CMS分為多個(gè)階段端考,只有stop the world的階段被計(jì)算到了Full GC的次數(shù)和時(shí)間,而和業(yè)務(wù)線程并發(fā)的GC的次數(shù)和時(shí)間則不被認(rèn)為是Full GC

  5. 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í)感悟以及筆記

虛擬機(jī)總結(jié)

什么是HotSpot VM & 深入理解Java虛擬機(jī) JVM

詳細(xì)介紹Java虛擬機(jī)(JVM)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市提岔,隨后出現(xiàn)的幾起案子仙蛉,更是在濱河造成了極大的恐慌,老刑警劉巖碱蒙,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荠瘪,死亡現(xiàn)場(chǎng)離奇詭異夯巷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)哀墓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)趁餐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人篮绰,你說(shuō)我怎么就攤上這事后雷。” “怎么了吠各?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵臀突,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贾漏,道長(zhǎng)候学,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任磕瓷,我火速辦了婚禮,結(jié)果婚禮上念逞,老公的妹妹穿的比我還像新娘困食。我一直安慰自己,他們只是感情好翎承,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布硕盹。 她就那樣靜靜地躺著,像睡著了一般叨咖。 火紅的嫁衣襯著肌膚如雪瘩例。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天甸各,我揣著相機(jī)與錄音垛贤,去河邊找鬼。 笑死趣倾,一個(gè)胖子當(dāng)著我的面吹牛聘惦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播儒恋,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼善绎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了诫尽?” 一聲冷哼從身側(cè)響起禀酱,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牧嫉,沒(méi)想到半個(gè)月后剂跟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年浩聋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了观蜗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衣洁,死狀恐怖墓捻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坊夫,我是刑警寧澤砖第,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站环凿,受9級(jí)特大地震影響梧兼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜智听,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一羽杰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧到推,春花似錦考赛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捣卤,卻和暖如春忍抽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背董朝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工鸠项, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人子姜。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓锈锤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闲询。 傳聞我的和親對(duì)象是個(gè)殘疾皇子久免,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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