一味廊、內(nèi)存回收的關(guān)注區(qū)域
JMM章節(jié)中介紹了Java虛擬機(jī)內(nèi)存模型的幾個(gè)區(qū)域介汹,對(duì)于程序計(jì)數(shù)器巡雨、虛擬機(jī)棧和本地方法棧都是線程私有的棍辕,伴隨著線程由生到滅暮现,這幾個(gè)區(qū)域的內(nèi)存分配這回收都有一定確定性(因?yàn)樗純?nèi)存大小基本是編譯可知的),因此楚昭,內(nèi)存回收主要的關(guān)注對(duì)象是堆和方法區(qū)栖袋。我們只有在程序運(yùn)行時(shí)才能確定會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的抚太。垃圾收集器主要關(guān)注的是這部分內(nèi)存塘幅。
二、對(duì)象可被回收的判斷標(biāo)準(zhǔn)
-
引用計(jì)數(shù)法
思想:就是對(duì)每個(gè)對(duì)象添加一個(gè)引用計(jì)數(shù)器尿贫,使用該算法的微軟的com技術(shù)等
弊端:這種方式存在一個(gè)問(wèn)題就是循環(huán)引用电媳,導(dǎo)致內(nèi)存泄露 -
可達(dá)性分析法
思想:就是通過(guò)一系列稱為GC Roots的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索帅霜,搜索所走過(guò)的路徑稱為引用鏈。
Java語(yǔ)言中呼伸,可以作為GC Roots對(duì)象包括下面幾種:
- 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
- 本地方法棧中(Native方法)引用的對(duì)象
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
-
引用相關(guān)知識(shí)
一般意義上講身冀,引用就代表了一個(gè)對(duì)象的內(nèi)存地址或者存有沒(méi)有對(duì)象地址的句柄,但是這種定義導(dǎo)致一個(gè)對(duì)象只存在引用和未被引用兩種狀態(tài)括享。而在實(shí)際應(yīng)用中搂根,一個(gè)對(duì)象需要更多的狀態(tài),來(lái)提高效率和優(yōu)化性能铃辖。因此剩愧,Java引用分為4種:
- 強(qiáng)引用:代碼中最常見(jiàn)的 Object obj = new Object();只要引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收被引用的對(duì)象娇斩。
- 軟引用:用來(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)存溢出異常。通過(guò)SoftReference來(lái)實(shí)現(xiàn)丰介。
- 弱引用:也是用來(lái)描述非必須的對(duì)象背蟆。弱引用只能生存到下一次垃圾收集之前。當(dāng)垃圾收集器開(kāi)始工作時(shí)哮幢,無(wú)論內(nèi)存是否足夠带膀,都會(huì)回收這部分內(nèi)存。通過(guò)WeakReference來(lái)實(shí)現(xiàn)橙垢。
- 虛引用:一個(gè)對(duì)象是否有虛引用垛叨,完全不會(huì)對(duì)其生存周期構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例钢悲。為一個(gè)對(duì)象設(shè)置虛引用的唯一目的就是能在這個(gè)對(duì)象被垃圾收集器回收時(shí)能夠收到一個(gè)系統(tǒng)通知点额。通過(guò)PhantomReference來(lái)實(shí)現(xiàn)。
-
對(duì)象是否直接死亡
對(duì)于可達(dá)性分析算法中不可達(dá)的對(duì)象莺琳,也不是“非死不可”还棱,要真正宣告對(duì)象死亡,必須經(jīng)歷兩次標(biāo)記過(guò)程惭等。如果可達(dá)性分析不可達(dá)時(shí)珍手,對(duì)象會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選標(biāo)準(zhǔn)是對(duì)象是否有必要執(zhí)行finalize()方法辞做。當(dāng)對(duì)象沒(méi)有覆蓋finalize()方法琳要,或者finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過(guò),虛擬機(jī)都將這兩種情況視為沒(méi)有必要執(zhí)行秤茅。如果這個(gè)對(duì)象被判定位必要執(zhí)行finalize()方法稚补,則會(huì)被放在一個(gè)F-Queue隊(duì)列中,并且會(huì)在稍后由一個(gè)虛擬機(jī)自建的Finalize線程去執(zhí)行它框喳。一個(gè)對(duì)象的finalize()方法只會(huì)被執(zhí)行一次课幕,一個(gè)對(duì)象可以在finalize()完成一次自救,并且只能自救一次五垮。
三乍惊、垃圾回收
-
回收方法區(qū)
方法區(qū)的回收對(duì)象主要是廢棄常量和無(wú)用的類。對(duì)于一個(gè)字符串常量“hello”,如果當(dāng)前系統(tǒng)中沒(méi)有任何一個(gè)String對(duì)象叫做“hello”放仗,那么就認(rèn)為沒(méi)有任何對(duì)象引用常量池中的“hello”常量润绎,這時(shí)該常量就是廢棄常量。無(wú)用的類的判斷標(biāo)準(zhǔn)則比較復(fù)雜,需要滿足一下條件:
- 該類的所有實(shí)例都已經(jīng)被回收莉撇,也就是Java堆中不存在該類的任何實(shí)例呢蛤。
- 加載該類的ClassLoader已經(jīng)被回收。
- 該類對(duì)應(yīng)的java.lang.Class對(duì)象沒(méi)有任何地方引用稼钩,無(wú)法在任何地方通過(guò)反射來(lái)訪問(wèn)該類的方法顾稀。
滿足上面三個(gè)條件的無(wú)用類可以被回收,但是并不是必然回收坝撑。還需要看虛擬機(jī)參數(shù)設(shè)置
-
垃圾收集算法
- 標(biāo)記-清除算法
思想:首先標(biāo)記出所偶遇需要回收的對(duì)象静秆,標(biāo)記完成之后進(jìn)行統(tǒng)一回收。
弊端:標(biāo)記和清除的效率很低巡李,另外就是會(huì)產(chǎn)生大量不連續(xù)的碎片抚笔,碎片過(guò)多可能會(huì)導(dǎo)致后續(xù)對(duì)象分配內(nèi)存時(shí),無(wú)法找到適合內(nèi)存空間而進(jìn)一步促發(fā)下一次垃圾收集動(dòng)作 - 復(fù)制算法
將可用內(nèi)存一份為二侨拦,每次使用其中一塊殊橙,當(dāng)一塊內(nèi)存使用完之后,將還存活的對(duì)象復(fù)制到另外一塊上狱从,然后將使用的這一塊給一次清理掉膨蛮。
弊端:將可用內(nèi)存空間一半空閑,代價(jià)過(guò)高
實(shí)際使用中大多數(shù)對(duì)象都朝生夕滅季研,所以優(yōu)化方法就是講內(nèi)存分配默認(rèn)Eden敞葛、和兩塊相等的Survivor空間8:1:1,每次垃圾回收時(shí)將還存活的對(duì)象復(fù)制到空閑的那一塊survivor中与涡,這樣每次就只會(huì)浪費(fèi)10%的內(nèi)存空間惹谐。當(dāng)然,當(dāng)survivor不夠保存剩余存活的對(duì)象時(shí)驼卖,需要到老年代擔(dān)保氨肌,也就是當(dāng)超出時(shí),將對(duì)象放到老年代酌畜。 - 標(biāo)記-整理算法
標(biāo)記過(guò)程和標(biāo)記-清除算法一樣怎囚,只是在清除時(shí),會(huì)將所有存活的對(duì)象移動(dòng)到一端桥胞,然后直接清理端外界的的內(nèi)存恳守。
-
垃圾收集器
- Serial收集器
這個(gè)收集器是一個(gè)單線程的收集器,這里單線程的意思是當(dāng)GC線程開(kāi)始工作時(shí)埠戳,其他的用戶線程都必須暫停井誉,當(dāng)?shù)竭_(dá)安全點(diǎn)之后蕉扮,GC線程新生代中采用復(fù)制算法整胃,老年代中采用標(biāo)記-整理算法進(jìn)行垃圾回收
弊端:需要暫停,非常影響體驗(yàn)
優(yōu)勢(shì):簡(jiǎn)單高效喳钟,不會(huì)有線程交互的開(kāi)銷屁使,如果停頓時(shí)間控制得當(dāng)在岂,不要頻繁發(fā)生,則還可以接受蛮寂,一般用于運(yùn)行在Client模式的虛擬機(jī)蔽午。 - ParNew收集器
多線程版本的Serial收集器。使用多線程來(lái)進(jìn)行垃圾回收酬蹋。許多運(yùn)行在server模式的虛擬機(jī)首選新生代收集器及老,原因是,除了Serial以外范抓,只有它可以與CMS收集器配合使用骄恶。 - Parallel Scavenge收集器
CMS等收集器的關(guān)注點(diǎn)是如何減少垃圾回收時(shí),用戶線程的等待時(shí)間匕垫,而Parallel Scavenge收集器則是達(dá)到一個(gè)可控制的吞吐量 吞吐量 = 執(zhí)行用戶代碼時(shí)間/(執(zhí)行用戶代碼時(shí)間+垃圾收集時(shí)間)僧鲁。停頓時(shí)間短適合交互性比較強(qiáng),需要良好的響應(yīng)速度來(lái)提升用戶體驗(yàn)象泵,而高吞吐量則可以高效的利用cpu時(shí)間寞秃,盡快完成程序的運(yùn)算任務(wù)。 - Serial Old收集器
Serial收集器的老年代版本偶惠,使用標(biāo)記-整理方法春寿,一個(gè)是與Parallel Scavenge配合使用,第二點(diǎn)就是作為CMS的后備方案 - Parallel Old收集器
Parallel Scavenge的老年代版本洲鸠,它出現(xiàn)之前堂淡,新生代的收集器Parallel只能和Serial Old配合使用 - CMS收集器
這種收集器主要獲取最短回收停頓時(shí)間為目標(biāo),主要用于互聯(lián)網(wǎng)站等系統(tǒng)的服務(wù)端扒腕。標(biāo)記-清除算法實(shí)現(xiàn)绢淀。其垃圾收集的過(guò)程分為四步:
初始標(biāo)記:主要標(biāo)記GC root能夠直接關(guān)聯(lián)的對(duì)象,時(shí)間短
并發(fā)標(biāo)記:GC root tracing的過(guò)程瘾腰,時(shí)間長(zhǎng)皆的,但是與用戶線程并發(fā)執(zhí)行
重新標(biāo)記:修正并發(fā)標(biāo)記過(guò)程中因用戶程序繼續(xù)運(yùn)用而產(chǎn)生變動(dòng)的那一部分對(duì)象,時(shí)間短
并發(fā)清除:時(shí)間長(zhǎng)蹋盆,與用戶線程并發(fā)執(zhí)行
缺點(diǎn):對(duì)cpu資源非常敏感费薄,因?yàn)椴l(fā)執(zhí)行階段會(huì)占用線程執(zhí)行的cpu資源,當(dāng)cpu較少時(shí)會(huì)導(dǎo)致系統(tǒng)吞吐量很低栖雾。無(wú)法處理浮動(dòng)的垃圾楞抡,可能會(huì)出現(xiàn)(concurrent mode failure)失敗而導(dǎo)致另一次full gc的出現(xiàn),在并發(fā)清除階段用戶線程還會(huì)生產(chǎn)垃圾析藕。還有一個(gè)缺點(diǎn)就是標(biāo)記-清除召廷,會(huì)產(chǎn)生大量空間碎片。 - G1收集器
是一款面向服務(wù)端的垃圾收集器,特點(diǎn):并發(fā)和并行竞慢,分代收集先紫,空間整合,整體上看像標(biāo)記-整理筹煮,局部像復(fù)制遮精,可預(yù)測(cè)的停頓。它將整個(gè)堆分為多個(gè)大小相等的區(qū)域败潦,雖然保留了新生代和老年代的概念本冲,但是不再是隔離的了。跟蹤每個(gè)區(qū)域垃圾堆積的大小劫扒,每次回收時(shí)眼俊,優(yōu)先回收價(jià)值最大的region。
垃圾收集的步驟:
初始標(biāo)記:
并發(fā)標(biāo)記:
最終標(biāo)記:
篩選回收:
四個(gè)過(guò)程和cms垃圾收集器很類似粟关,最后一步不同在于疮胖,g1是挑選最具有回收的價(jià)值的區(qū)域開(kāi)始回收
4.GC 日志
關(guān)鍵點(diǎn):GC、Full GC闷板、回收前的容量澎灸、回收后的容量、總的容量遮晚、回收的區(qū)域性昭、回收時(shí)間a