垃圾回收機(jī)制(GC)
簡(jiǎn)介:
JVM的垃圾回收機(jī)制稱(chēng)為GC,眾所周知化戳,java語(yǔ)言不需要像c++那樣需要自己申請(qǐng)內(nèi)存涧至,自己釋放內(nèi)存搀别,它可以自動(dòng)回收可以釋放的內(nèi)存資源急侥。如果不進(jìn)行垃圾回收,內(nèi)存遲早都會(huì)被消耗空姚垃,因?yàn)槲覀冊(cè)诓粩嗟姆峙鋬?nèi)存空間而不進(jìn)行回收念链。除非內(nèi)存無(wú)限大,我們可以任性的分配而不回收积糯,但是事實(shí)并非如此掂墓。所以,垃圾回收是必須的看成。
JVM內(nèi)存運(yùn)行時(shí)數(shù)據(jù)區(qū):
結(jié)構(gòu)圖:
image.png基礎(chǔ)知識(shí):
堆(heap):
最大的君编,最重要的一塊區(qū)域,稱(chēng)為邏輯堆川慌,主要用來(lái)存放對(duì)象實(shí)例與數(shù)組吃嘿,對(duì)于所有的線程來(lái)說(shuō)他是全局共享的,對(duì)于Heap堆區(qū)是動(dòng)態(tài)分配內(nèi)存的梦重,所以空間大小和生命周期都不是明確的兑燥,而GC的主要作用就是自動(dòng)釋放邏輯堆里實(shí)例對(duì)象所占的內(nèi)存,而在邏輯堆中還分為新生代與老年代琴拧,用來(lái)區(qū)分對(duì)象的存活時(shí)間降瞳,在新生代中還被細(xì)致的分為 Eden SurvivorFrom以及SurvivorTo這三部分。
方法區(qū)(Method Area):
主要存儲(chǔ)(類(lèi)加載器)ClassLoader加載的類(lèi)信息艾蓝,可理解為已經(jīng)編譯好的代碼儲(chǔ)存區(qū)力崇,所以存儲(chǔ)包括類(lèi)的元數(shù)據(jù)斗塘、常量池赢织、字段、靜態(tài)變量與方法內(nèi)的局部變量以及編譯好的字節(jié)碼等等馍盟。在Hotspot里將它稱(chēng)之為永生代于置。
棧(stack):
全稱(chēng)為虛擬機(jī)棧,主要存儲(chǔ)基本數(shù)據(jù)類(lèi)型,以及對(duì)象的引用八毯,私有線程搓侄。在每一個(gè)對(duì)象被創(chuàng)建的時(shí)候,在堆棧區(qū)都有一個(gè)對(duì)他的引用话速。
Object obj = new Object();
左邊的Object obj 等于在堆棧區(qū)申請(qǐng)了一個(gè)內(nèi)存讶踪,也就是對(duì)類(lèi)的引用,而 new Object()則是生成了一個(gè)實(shí)例泊交,= 則是將對(duì)象的內(nèi)容可通過(guò)obj進(jìn)行訪問(wèn)乳讥,在Java里都是通過(guò)引用來(lái)操縱對(duì)象。
pc寄存器(PC Register):
在多線程中廓俭,系統(tǒng)需要給每一個(gè)線程分配一個(gè)編號(hào)云石,這個(gè)時(shí)候才會(huì)需要到寄存器。
四大引用狀態(tài)(JDK 1.2之后):
1.強(qiáng)引用:
代碼中普遍存在的類(lèi)似"Object obj = new Object()"這類(lèi)的引用研乒,只要強(qiáng)引用還存在汹忠,除非程序員手動(dòng)代碼解除強(qiáng)引用,釋放資源雹熬。否值宽菜,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。
2.軟引用:
描述有些還有用但并非必需的對(duì)象竿报。在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前赋焕,將會(huì)把這些對(duì)象列進(jìn)回收范圍進(jìn)行二次回收。如果這次回收還沒(méi)有足夠的內(nèi)存仰楚,才會(huì)拋出內(nèi)存溢出異常隆判。Java中的類(lèi)SoftReference表示軟引用。
3.弱引用:
描述非必需對(duì)象僧界。被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾回收之前侨嘀,垃圾收集器工作之后,無(wú)論當(dāng)前內(nèi)存是否足夠捂襟,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象咬腕。Java中的類(lèi)WeakReference表示弱引用。
4.虛引用:
這個(gè)引用存在的唯一目的就是在這個(gè)對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知葬荷,被虛引用關(guān)聯(lián)的對(duì)象涨共,和其生存時(shí)間完全沒(méi)關(guān)系。Java中的類(lèi)PhantomReference表示虛引用宠漩。
兩大查找回收對(duì)象法:
1.引用計(jì)數(shù)法:
給對(duì)象中添加一個(gè)引用計(jì)數(shù)器举反,每當(dāng)一個(gè)地方引用這個(gè)對(duì)象時(shí),計(jì)數(shù)器值+1扒吁;當(dāng)引用失效時(shí)火鼻,計(jì)數(shù)器值-1。任何時(shí)刻計(jì)數(shù)值為0的對(duì)象就是不可能再被使用的。這種算法使用場(chǎng)景很多魁索,但是融撞,Java中卻沒(méi)有使用這種算法,因?yàn)檫@種算法很難解決對(duì)象之間相互引用的情況粗蔚。
/** * 虛擬機(jī)參數(shù):-verbose:gc */ public class ReferenceCountingGC { private Object instance = null; private static final int _1MB = 1024 * 1024; /** 這個(gè)成員屬性唯一的作用就是占用一點(diǎn)內(nèi)存 */ private byte[] bigSize = new byte[2 * _1MB]; public static void main(String[] args) { ReferenceCountingGC objectA = new ReferenceCountingGC(); ReferenceCountingGC objectB = new ReferenceCountingGC(); objectA.instance = objectB; objectB.instance = objectA; objectA = null; objectB = null; System.gc(); } } /* 運(yùn)行結(jié)果: [GC 4417K->288K(61440K), 0.0013498 secs] [Full GC 288K->194K(61440K), 0.0094790 secs] */
兩個(gè)對(duì)象相互引用著尝偎,但是虛擬機(jī)還是把這兩個(gè)對(duì)象回收掉了,這也說(shuō)明虛擬機(jī)并不是通過(guò)引用計(jì)數(shù)法來(lái)判定對(duì)象是否存活的鹏控。
2.可達(dá)性分析法:
通過(guò)一系列稱(chēng)為“GC Roots”的對(duì)象作為起始點(diǎn)冬念,從這些節(jié)點(diǎn)向下搜索,搜索所走過(guò)的路徑稱(chēng)為引用鏈牧挣,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈(即GC Roots到對(duì)象不可達(dá))時(shí)急前,則證明此對(duì)象是不可用的。
GC Roots的對(duì)象包括下面幾種:
1.虛擬機(jī)棧(棧幀中的局部變量區(qū)瀑构,也叫局部變量表)中的引用對(duì)象裆针。
2.方法區(qū)中的類(lèi)靜態(tài)屬性引用的對(duì)象。
3.方法區(qū)中常量引用的對(duì)象寺晌。
4.本地方法棧中JNI(Native方法)引用的對(duì)象世吨。image.png
由圖可知,obj8呻征、obj9耘婚、obj10都沒(méi)有到GCRoots對(duì)象的引用鏈,即便obj9和obj10之間有引用鏈陆赋,他們還是會(huì)被當(dāng)成垃圾處理沐祷,可以進(jìn)行回收。
注意:
對(duì)于可達(dá)性分析算法而言攒岛,未到達(dá)的對(duì)象并非是“非死不可”的赖临,若要宣判一個(gè)對(duì)象死亡,至少需要經(jīng)歷兩次標(biāo)記階段灾锯。
1.如果對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與GCRoots相連的引用鏈兢榨,則該對(duì)象被第一次標(biāo)記并進(jìn)行一次篩選,篩選條件為是否有必要執(zhí)行該對(duì)象的finalize方法顺饮,若對(duì)象沒(méi)有覆蓋finalize方法或者該finalize方法是否已經(jīng)被虛擬機(jī)執(zhí)行過(guò)了吵聪,則均視作不必要執(zhí)行該對(duì)象的finalize方法,即該對(duì)象將會(huì)被回收兼雄。反之吟逝,若對(duì)象覆蓋了finalize方法并且該finalize方法并沒(méi)有被執(zhí)行過(guò),那么君旦,這個(gè)對(duì)象會(huì)被放置在一個(gè)叫F-Queue的隊(duì)列中澎办,之后會(huì)由虛擬機(jī)自動(dòng)建立的嘲碱、優(yōu)先級(jí)低的Finalizer線程去執(zhí)行金砍,而虛擬機(jī)不必要等待該線程執(zhí)行結(jié)束局蚀,即虛擬機(jī)只負(fù)責(zé)建立線程,其他的事情交給此線程去處理恕稠。
2.對(duì)F-Queue中對(duì)象進(jìn)行第二次標(biāo)記琅绅,如果對(duì)象在finalize方法中拯救了自己,即關(guān)聯(lián)上了GCRoots引用鏈鹅巍,如把this關(guān)鍵字賦值給其他變量千扶,那么在第二次標(biāo)記的時(shí)候該對(duì)象將從“即將回收”的集合中移除,如果對(duì)象還是沒(méi)有拯救自己骆捧,那就會(huì)被回收澎羞。
如下代碼演示了一個(gè)對(duì)象如何在finalize方法中拯救了自己,然而敛苇,它只能拯救自己一次妆绞,第二次就被回收了。
/* * 此代碼演示了兩點(diǎn): * 1.對(duì)象可以再被GC時(shí)自我拯救 * 2.這種自救的機(jī)會(huì)只有一次枫攀,因?yàn)橐粋€(gè)對(duì)象的finalize()方法最多只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次 * */ public class FinalizeEscapeGC { public String name; public static FinalizeEscapeGC SAVE_HOOK = null; public FinalizeEscapeGC(String name) { this.name = name; } public void isAlive() { System.out.println("我還活著括饶。)"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method 執(zhí)行"); System.out.println(this); FinalizeEscapeGC.SAVE_HOOK = this; } @Override public String toString() { return name; } public static void main(String[] args) throws InterruptedException { SAVE_HOOK = new FinalizeEscapeGC("jimyoungwei"); System.out.println(SAVE_HOOK); // 對(duì)象第一次拯救自己 SAVE_HOOK = null; System.out.println(SAVE_HOOK); System.gc(); // 因?yàn)閒inalize方法優(yōu)先級(jí)很低,所以暫停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("我已消亡来涨。"); } // 下面這段代碼與上面的完全相同,但是這一次自救卻失敗了 // 一個(gè)對(duì)象的finalize方法只會(huì)被調(diào)用一次 SAVE_HOOK = null; System.gc(); // 因?yàn)閒inalize方法優(yōu)先級(jí)很低图焰,所以暫停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("我已消亡。"); } } } /* 運(yùn)行結(jié)果: jimyoungwei null finalize method 執(zhí)行 jimyoungwei 我還活著蹦掐。 我已消亡技羔。 */
對(duì)象的finalize方法最多被虛擬機(jī)調(diào)用一次,一個(gè)堆對(duì)象的this(放在局部變量表中的第一項(xiàng))引用會(huì)永遠(yuǎn)存在卧抗,在方法體內(nèi)可以將this引用賦值給其他變量堕阔,這樣堆中對(duì)象就可以被其他變量所引用,即不會(huì)被回收颗味。
四大垃圾收集算法:
1.標(biāo)記-消除(Mark-Sweep)算法:
最基礎(chǔ)的算法超陆,分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象浦马。
不足:主要體現(xiàn)在效率和空間:從效率的角度講时呀,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高;從空間的角度講晶默,標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片谨娜, 內(nèi)存碎片太多可能會(huì)導(dǎo)致以后程序運(yùn)行過(guò)程中在需要分配較大對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)一次垃圾收集動(dòng)作磺陡。
標(biāo)記-清除算法執(zhí)行過(guò)程如圖:
image.png2.復(fù)制(Copying)算法:
復(fù)制算法是為了解決效率問(wèn)題而出現(xiàn)的趴梢,它將可用的內(nèi)存分為兩塊漠畜,每次只用其中一塊,當(dāng)這一塊內(nèi)存用完了坞靶,就將還存活著的對(duì)象復(fù)制到另外一塊上面憔狞,然后再把已經(jīng)使用過(guò)的內(nèi)存空間一次性清理掉。這樣每次只需要對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收彰阴,內(nèi)存分配時(shí)也不需要考慮內(nèi)存碎片等復(fù)雜情況瘾敢,只需要移動(dòng)指針,按照順序分配即可尿这。
不足:內(nèi)存縮小為了原來(lái)的一半簇抵。
復(fù)制算法執(zhí)行過(guò)程如圖:
image.png現(xiàn)在的商用虛擬機(jī)都采用這種算法來(lái)回收新生代,但1:1的比例非常不科學(xué)射众,因此新生代的內(nèi)存被劃分為一塊較大的Eden空間和兩塊較小的Survivor空間碟摆,每次使用Eden和其中一塊Survivor。每次回收時(shí)叨橱,將Eden和Survivor中還存活著的對(duì)象一次性復(fù)制到另外一塊Survivor空間上典蜕,最后清理掉Eden和剛才用過(guò)的Survivor空間。HotSpot虛擬機(jī)默認(rèn)Eden區(qū)和Survivor區(qū)的比例為8:1雏逾,每次新生代中可用內(nèi)存空間為整個(gè)新生代容量的90%嘉裤。當(dāng)Survivor空間不夠用時(shí),需要依賴(lài)?yán)夏甏M(jìn)行分配擔(dān)保(Handle Promotion)栖博。
3.標(biāo)記-整理(Mark-Compact)算法:
復(fù)制算法在對(duì)象存活率較高的場(chǎng)景下要進(jìn)行大量的復(fù)制操作屑宠,效率很低。萬(wàn)一對(duì)象100%存活仇让,那么需要有額外的空間進(jìn)行分配擔(dān)保典奉。老年代都是不易被回收的對(duì)象,對(duì)象存活率高丧叽,因此一般不能直接選用復(fù)制算法卫玖。根據(jù)老年代的特點(diǎn),有人提出了另外一種標(biāo)記-整理算法踊淳,過(guò)程與標(biāo)記-清除算法一樣假瞬,不過(guò)不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活對(duì)象都向一端移動(dòng)迂尝,然后直接清理掉邊界以外的內(nèi)存脱茉。
標(biāo)志-整理算法執(zhí)行圖:
image.png4.分代收集算法:
分代收集算法執(zhí)行圖:
image.png小結(jié):
根據(jù)對(duì)象的生命周期的不同將內(nèi)存劃分為幾塊,然后根據(jù)各塊的特點(diǎn)采用最適當(dāng)?shù)氖占惴⒖4笈鷮?duì)象死去琴许、少量對(duì)象存活的(新生代),使用復(fù)制算法溉躲,復(fù)制成本低榜田;對(duì)象存活率高益兄、沒(méi)有額外空間進(jìn)行分配擔(dān)保的(老年代),采用標(biāo)記-清理算法或者標(biāo)記-整理算法箭券。
常見(jiàn)垃圾收集器:
1.Serial收集器:
最基本净捅、發(fā)展歷史最久的收集器,這個(gè)收集器是一個(gè)采用復(fù)制算法的單線程的收集器邦鲫,單線程一方面意味著它只會(huì)使用一個(gè)CPU或一條線程去完成垃圾收集工作灸叼,另一方面也意味著它進(jìn)行垃圾收集時(shí)必須暫停其他線程的所有工作神汹,直到它收集結(jié)束為止庆捺。后者意味著,在用戶(hù)不可見(jiàn)的情況下要把用戶(hù)正常工作的線程全部停掉屁魏,這對(duì)很多應(yīng)用是難以接受的滔以。
不過(guò)實(shí)際上到目前為止,Serial收集器依然是虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)新生代收集器氓拼,因?yàn)樗?jiǎn)單而高效你画。用戶(hù)桌面應(yīng)用場(chǎng)景中,分配給虛擬機(jī)管理的內(nèi)存一般來(lái)說(shuō)不會(huì)很大桃漾,收集幾十兆甚至一兩百兆的新生代停頓時(shí)間在幾十毫秒最多一百毫秒坏匪,只要不是頻繁發(fā)生,這點(diǎn)停頓是完全可以接受的撬统。
Serial收集器運(yùn)行過(guò)程如下圖所示:
image.png說(shuō)明:
1.需要STW(Stop The World)适滓,停頓時(shí)間長(zhǎng)。
2.簡(jiǎn)單高效恋追,對(duì)于單個(gè)CPU環(huán)境而言凭迹,Serial收集器由于沒(méi)有線程交互開(kāi)銷(xiāo),可以獲取最高的單線程收集效率苦囱。2.ParNew收集器:
ParNew收集器其實(shí)就是Serial收集器的多線程版本嗅绸,除了使用多條線程進(jìn)行垃圾收集外,其余行為和Serial收集器完全一樣撕彤,包括使用的也是復(fù)制算法鱼鸠。ParNew收集器除了多線程以外和Serial收集器并沒(méi)有太多創(chuàng)新的地方,但是它卻是Server模式下的虛擬機(jī)首選的新生代收集器羹铅,其中有一個(gè)很重要的和性能無(wú)關(guān)的原因是蚀狰,除了Serial收集器外,目前只有它能與CMS收集器配合工作睦裳。
ParNew收集器在單CPU的環(huán)境中絕對(duì)不會(huì)有比Serial收集器更好的效果造锅,甚至由于線程交互的開(kāi)銷(xiāo),該收集器在兩個(gè)CPU的環(huán)境中都不能百分之百保證可以超越Serial收集器廉邑。隨著可用CPU數(shù)量的增加哥蔚,它對(duì)于GC時(shí)系統(tǒng)資源的有效利用還是很有好處的倒谷。它默認(rèn)開(kāi)啟的收集線程數(shù)與CPU數(shù)量相同,在CPU數(shù)量非常多的情況下糙箍,可以使用-XX:ParallelGCThreads參數(shù)來(lái)限制垃圾收集的線程數(shù)渤愁。
ParNew收集器運(yùn)行過(guò)程如下圖所示:
image.png3.Parallel Scavenge收集器:
Parallel Scavenge收集器也是一個(gè)新生代收集器,也是用復(fù)制算法的收集器深夯,也是并行的多線程收集器抖格。CMS等收集器的關(guān)注點(diǎn)是盡可能縮短垃圾收集時(shí)用戶(hù)線程的停頓時(shí)間,而Parallel Scavenge收集器的目標(biāo)則是達(dá)到一個(gè)可控制的吞吐量咕晋。
吞吐量:CPU用于運(yùn)行用戶(hù)代碼時(shí)間與CPU總消耗時(shí)間的比值雹拄,即吞吐量=運(yùn)行用戶(hù)代碼時(shí)間/(運(yùn)行用戶(hù)代碼時(shí)間+垃圾收集時(shí)間),虛擬機(jī)總運(yùn)行100分鐘掌呜,垃圾收集1分鐘滓玖,那吞吐量就是99%。
Parallel Scavenge收集器是虛擬機(jī)運(yùn)行在Server模式下的默認(rèn)垃圾收集器质蕉。
4.Serial Old收集器:
Serial收集器的老年代版本势篡,同樣是一個(gè)單線程收集器,使用“標(biāo)記-整理算法”模暗,這個(gè)收集器的主要意義也是在于給Client模式下的虛擬機(jī)使用禁悠。
5.Parallel Old收集器:
Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法兑宇。在注重吞吐量以及CPU資源敏感的場(chǎng)合碍侦,都可以?xún)?yōu)先考慮Parallel Scavenge收集器+Parallel Old收集器的組合。
運(yùn)行過(guò)程如下圖所示:
image.png6.CMS收集器:
CMS(Conrrurent Mark Sweep)收集器是以獲取最短回收停頓時(shí)間為目標(biāo)的收集器顾孽。使用標(biāo)記 - 清除算法祝钢,收集過(guò)程分為如下四步:
1.初始標(biāo)記,標(biāo)記GCRoots能直接關(guān)聯(lián)到的對(duì)象若厚,時(shí)間很短拦英。
2.并發(fā)標(biāo)記,進(jìn)行GCRoots Tracing(可達(dá)性分析)過(guò)程测秸,時(shí)間很長(zhǎng)疤估。
3.重新標(biāo)記,修正并發(fā)標(biāo)記期間因用戶(hù)程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄霎冯,時(shí)間較長(zhǎng)铃拇。
4.并發(fā)清除,回收內(nèi)存空間沈撞,時(shí)間很長(zhǎng)慷荔。
其中,并發(fā)標(biāo)記與并發(fā)清除兩個(gè)階段耗時(shí)最長(zhǎng)缠俺,但是可以與用戶(hù)線程并發(fā)執(zhí)行显晶。運(yùn)行過(guò)程如下圖所示:
image.png說(shuō)明:
1.對(duì)CPU資源非常敏感贷岸,可能會(huì)導(dǎo)致應(yīng)用程序變慢,吞吐率下降磷雇。2.無(wú)法處理浮動(dòng)垃圾偿警,因?yàn)樵诓l(fā)清理階段用戶(hù)線程還在運(yùn)行,自然就會(huì)產(chǎn)生新的垃圾唯笙,而在此次收集中無(wú)法收集他們螟蒸,只能留到下次收集,這部分垃圾為浮動(dòng)垃圾崩掘,同時(shí)七嫌,由于用戶(hù)線程并發(fā)執(zhí)行,所以需要預(yù)留一部分老年代空間提供并發(fā)收集時(shí)程序運(yùn)行使用呢堰。
3.由于采用的標(biāo)記 - 清除算法抄瑟,會(huì)產(chǎn)生大量的內(nèi)存碎片凡泣,不利于大對(duì)象的分配枉疼,可能會(huì)提前觸發(fā)一次Full GC。虛擬機(jī)提供了-XX:+UseCMSCompactAtFullCollection參數(shù)來(lái)進(jìn)行碎片的合并整理過(guò)程鞋拟,這樣會(huì)使得停頓時(shí)間變長(zhǎng)骂维,虛擬機(jī)還提供了一個(gè)參數(shù)配置,-XX:+CMSFullGCsBeforeCompaction贺纲,用于設(shè)置執(zhí)行多少次不壓縮的Full GC后航闺,接著來(lái)一次帶壓縮的GC。
7.G1收集器:
與其他GC收集器相比猴誊,G1收集器有以下特點(diǎn):
1.并行和并發(fā)潦刃。使用多個(gè)CPU來(lái)縮短Stop The World停頓時(shí)間,與用戶(hù)線程并發(fā)執(zhí)行懈叹。
2.分代收集乖杠。獨(dú)立管理整個(gè)堆,但是能夠采用不同的方式去處理新創(chuàng)建對(duì)象和已經(jīng)存活了一段時(shí)間澄成、熬過(guò)多次GC的舊對(duì)象胧洒,以獲取更好的收集效果。
3.空間整合墨状∥缆基于標(biāo)記 - 整理算法,無(wú)內(nèi)存碎片產(chǎn)生肾砂。
4.可預(yù)測(cè)的停頓列赎。能簡(jiǎn)歷可預(yù)測(cè)的停頓時(shí)間模型,能讓使用者明確指定在一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段內(nèi)镐确,消耗在垃圾收集上的時(shí)間不得超過(guò)N毫秒包吝。在G1之前的垃圾收集器肛根,收集的范圍都是整個(gè)新生代或者老年代,而G1不再是這樣漏策。使用G1收集器時(shí)派哲,Java堆的內(nèi)存布局與其他收集器有很大差別,它將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)掺喻,雖然還保留有新生代和老年代的概念感耙,但新生代和老年代不再是物理隔離的了,它們都是一部分(可以不連續(xù))Region的集合。
理解GC日志:
[GC [DefNew: 310K->194K(2368K), 0.0269163 secs] 310K->194K(7680K), 0.0269513 secs] [Times: user=0.00 sys=0.00, real=0.03 secs] [GC [DefNew: 2242K->0K(2368K), 0.0018814 secs] 2242K->2241K(7680K), 0.0019172 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System) [Tenured: 2241K->193K(5312K), 0.0056517 secs] 4289K->193K(7680K), [Perm : 2950K->2950K(21248K)], 0.0057094 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 2432K, used 43K [0x00000000052a0000, 0x0000000005540000, 0x0000000006ea0000) eden space 2176K, 2% used [0x00000000052a0000, 0x00000000052aaeb8, 0x00000000054c0000) from space 256K, 0% used [0x00000000054c0000, 0x00000000054c0000, 0x0000000005500000) to space 256K, 0% used [0x0000000005500000, 0x0000000005500000, 0x0000000005540000) tenured generation total 5312K, used 193K [0x0000000006ea0000, 0x00000000073d0000, 0x000000000a6a0000) the space 5312K, 3% used [0x0000000006ea0000, 0x0000000006ed0730, 0x0000000006ed0800, 0x00000000073d0000) compacting perm gen total 21248K, used 2982K [0x000000000a6a0000, 0x000000000bb60000, 0x000000000faa0000) the space 21248K, 14% used [0x000000000a6a0000, 0x000000000a989980, 0x000000000a989a00, 0x000000000bb60000) No shared spaces configured.
說(shuō)明:
1.日志的開(kāi)頭“GC”僻澎、“Full GC”表示這次垃圾收集的停頓類(lèi)型,而不是用來(lái)區(qū)分新生代GC還是老年代GC的砸捏。如果有Full,則說(shuō)明本次GC停止了其他所有工作線程(Stop-The-World)∶昂冢看到Full GC的寫(xiě)法是“Full GC(System)”芒划,這說(shuō)明是調(diào)用System.gc()方法所觸發(fā)的GC拼苍。
2.“GC”中接下來(lái)的“[DefNew”表示GC發(fā)生的區(qū)域,這里顯示的區(qū)域名稱(chēng)與使用的GC收集器是密切相關(guān)的早处,例如上面樣例所使用的Serial收集器中的新生代名為“Default New Generation”,所以顯示的是“[DefNew”。如果是ParNew收集器,新生代名稱(chēng)就會(huì)變?yōu)椤癧ParNew”嚷往,意為“Parallel New Generation”。如果采用Parallel Scavenge收集器柠衅,那它配套的新生代稱(chēng)為“PSYoungGen”皮仁,老年代和永久代同理,名稱(chēng)也是由收集器決定的。
3.后面方括號(hào)內(nèi)部的“310K->194K(2368K)”贷祈,指的是該區(qū)域已使用的容量->GC后該內(nèi)存區(qū)域已使用的容量(該內(nèi)存區(qū)總?cè)萘?趋急。方括號(hào)外面的“310K->194K(7680K)”,則指的是GC前Java堆已使用的容量->GC后Java堆已使用的容量(Java堆總?cè)萘?势誊。
4.再往后“0.0269163 secs”表示該內(nèi)存區(qū)域GC所占用的時(shí)間呜达,單位是秒。最后的“[Times: user=0.00 sys=0.00 real=0.03 secs]”則更具體了粟耻,user表示用戶(hù)態(tài)消耗的CPU時(shí)間闻丑、內(nèi)核態(tài)消耗的CPU時(shí)間、操作從開(kāi)始到結(jié)束經(jīng)過(guò)的墻鐘時(shí)間勋颖。后面兩個(gè)的區(qū)別是嗦嗡,墻鐘時(shí)間包括各種非運(yùn)算的等待消耗,比如等待磁盤(pán)I/O饭玲、等待線程阻塞侥祭,而CPU時(shí)間不包括這些耗時(shí),但當(dāng)系統(tǒng)有多CPU或者多核的話茄厘,多線程操作會(huì)疊加這些CPU時(shí)間矾湃,所以如果看到user或sys時(shí)間超過(guò)real時(shí)間是完全正常的。
5.“Heap”后面就列舉出堆內(nèi)存目前各個(gè)年代的區(qū)域的內(nèi)存情況户秤。
上一篇:Java基礎(chǔ)(4)—異常與內(nèi)部類(lèi)
下一篇:Java基礎(chǔ)(6)—Java虛擬機(jī) JVM
精彩內(nèi)容不夠看睬关?更多精彩內(nèi)容,請(qǐng)到微信搜索 “危君子頻道” 訂閱號(hào)窑滞,每天更新琼牧,歡迎大家關(guān)注訂閱!