GC 垃圾回收
1靴姿、GC是什么?為什么要GC
GC:垃圾收集磁滚,GC能幫助我們釋放jvm內(nèi)存佛吓,可以一定程度避免OOM問(wèn)題,但是也無(wú)法完全避免垂攘。Java的GC是自動(dòng)工作的维雇,不像C++需要主動(dòng)調(diào)用。
當(dāng)new對(duì)象的時(shí)候晒他,GC就開(kāi)始監(jiān)控這個(gè)對(duì)象的地址大小和使用情況了吱型,通過(guò)可達(dá)性分析算法尋找不可達(dá)的對(duì)象然后進(jìn)行標(biāo)記看看是否需要GC回收掉釋放內(nèi)存。
2陨仅、你能保證GC執(zhí)行嗎唁影?
不能耕陷,我只能通過(guò)手動(dòng)執(zhí)行System.gc()方法通知GC執(zhí)行,但是他是否執(zhí)行的未知的据沈。
3哟沫、對(duì)象的引用類(lèi)型有哪幾種,分別介紹下
強(qiáng)引用
發(fā)生GC的時(shí)候不會(huì)回收強(qiáng)引用所關(guān)聯(lián)的對(duì)象锌介。比如new就是強(qiáng)引用嗜诀。軟引用
有用但非必須的對(duì)象,在OOM之前會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收孔祸,若第二次回收還沒(méi)有足夠的內(nèi)存隆敢,則會(huì)拋出OOM。也就是第一次快要發(fā)生OOM的時(shí)候不會(huì)立馬拋出OOM崔慧,而是會(huì)回收掉這些軟引用拂蝎,然后再看內(nèi)存是否足夠,若還不夠才會(huì)拋出OOM惶室。弱引用
有用但非必須的對(duì)象温自,比軟引用更弱一些,只要開(kāi)始GC皇钞,不管你內(nèi)存夠不夠悼泌,都會(huì)將 弱引用所關(guān)聯(lián)的對(duì)象給回收掉。虛引用
也叫幽靈引用/幻影引用夹界,無(wú)法通過(guò)虛引用獲得對(duì)象馆里,他的意義在于能在這個(gè)對(duì)象被GC掉時(shí)收到一個(gè)系統(tǒng)通知,僅此而已可柿。
4鸠踪、垃圾收集算法有哪些
標(biāo)記清除
分為兩步:標(biāo)記和清除。
首先需要標(biāo)記出所有需要回收的對(duì)象复斥,然后進(jìn)行清除回收變?yōu)榭捎脙?nèi)存营密。
缺點(diǎn):效率低,會(huì)產(chǎn)生垃圾碎片 永票。
復(fù)制算法
將可用堆內(nèi)存按照容量分為大小相等的兩塊,每次只用一塊滥沫,當(dāng)這塊內(nèi)存快用完了侣集,就將還存活著的對(duì)象復(fù)制到另一塊上面,然后再把已使用過(guò)的內(nèi)存一次清理掉兰绣。
年輕代from/to(s1/s2)采取的就是此種算法世分。老年代一般不會(huì)采取此種算法,因?yàn)槔夏甏际谴髮?duì)象且存活的久的缀辩,空間壓縮一半代價(jià)略高臭埋。
優(yōu)點(diǎn):效率較高踪央、不會(huì)產(chǎn)生碎片。
缺點(diǎn):將內(nèi)存縮小為原來(lái)的一半瓢阴,代價(jià)略高畅蹂。
標(biāo)記整理
分為兩步:標(biāo)記和整理。
整理其實(shí)也是兩步:整理+清除荣恐。
整理讓所有存活的對(duì)象都移動(dòng)到一端液斜,然后清理掉邊界以外的內(nèi)存。
優(yōu)點(diǎn):不會(huì)產(chǎn)生碎片問(wèn)題叠穆,適合年老代的大對(duì)象存儲(chǔ)少漆,不像復(fù)制算法那樣浪費(fèi)空間。
缺點(diǎn):效率趕不上復(fù)制算法硼被。
分代算法
并不是新算法示损,而是根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊,一般是新生代和老年代嚷硫,新生代基本采用復(fù)制算法检访,老年代采用標(biāo)記整理算法。
5论巍、為什么要分代
因?yàn)樵诓贿M(jìn)行對(duì)象存活時(shí)間區(qū)分的情況下烛谊,每次垃圾回收都是對(duì)整個(gè)堆空間進(jìn)行回收,花費(fèi)時(shí)間會(huì)相對(duì)較長(zhǎng)嘉汰,也有很多對(duì)象完全沒(méi)必要遍歷丹禀,比如大對(duì)象存活的時(shí)間更長(zhǎng),遍歷下來(lái)發(fā)現(xiàn)不需要回收鞋怀,這樣更浪費(fèi)時(shí)間双泪。所以才有了分代,分治的思想密似,進(jìn)行區(qū)域劃分焙矛,把不同生命周期的對(duì)象放在不同的區(qū)域,不同的區(qū)域采取最適合他的垃圾回收方式進(jìn)行回收残腌。
6村斟、分代垃圾回收是怎么工作的
分代回收基于這樣一個(gè)理念:不同的對(duì)象的生命周期是不一樣的,因此根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊抛猫,一般是新生代和老年代蟆盹,新生代基本采用復(fù)制算法,老年代采用標(biāo)記整理算法闺金。這樣來(lái)提高回收效率逾滥。
新生代執(zhí)行流程:
- 把 Eden + From Survivor(S1) 存活的對(duì)象放入 To Survivor(S2) 區(qū);
- 清空 Eden 和S1 區(qū)败匹;
- S1 和 S2 區(qū)交換寨昙,S1 變 S2讥巡,S2變S1。
每次在S1到S2移動(dòng)時(shí)都存活的對(duì)象舔哪,年齡就 +1欢顷,當(dāng)年齡到達(dá) 15(默認(rèn)配置是 15)時(shí),升級(jí)為老年代尸红。大對(duì)象也會(huì)直接進(jìn)入老年代吱涉。
老年代當(dāng)空間占用到達(dá)某個(gè)值之后就會(huì)觸發(fā)全局垃圾收回,一般使用標(biāo)記整理的執(zhí)行算法外里。
7怎爵、垃圾回收器有哪些
Serial
采取復(fù)制算法,用于新生代盅蝗,單線(xiàn)程收集器鳖链,所以在他工作時(shí)會(huì)產(chǎn)生StopTheWorld。單線(xiàn)程情況下效率更高墩莫,比如用于GUI小程序
ParNew
采取復(fù)制算法芙委,用于新生代,是Serial的多線(xiàn)程版本狂秦,多個(gè)GC線(xiàn)程同時(shí)工作灌侣,但是也會(huì)產(chǎn)生StopTheWorld,因?yàn)椴荒芎凸ぷ骶€(xiàn)程并行裂问。
Parallel Scavenge
采取復(fù)制算法侧啼,用于新生代,和ParNew一樣堪簿,所以也會(huì)產(chǎn)生STW痊乾,多線(xiàn)程收集器,他是吞吐量?jī)?yōu)先的收集器椭更,提供了很多參數(shù)來(lái)調(diào)節(jié)吞吐量哪审。
Serial Old
采取標(biāo)記整理算法,用于老年代虑瀑,單線(xiàn)程收集器湿滓,所以在他工作時(shí)會(huì)產(chǎn)生StopTheWorld。單線(xiàn)程情況下效率更高舌狗,比如用于GUI小程序
Parallel Old
采取標(biāo)記整理算法叽奥,用于老年代,Parallel Scavenge收集器的老年代版本把夸,吞吐量?jī)?yōu)先而线。
CMS
采取標(biāo)記清除算法铭污,老年代并行收集器恋日,號(hào)稱(chēng)以最短STW時(shí)間為目標(biāo)的收集器膀篮,并發(fā)高、停頓低岂膳、STW時(shí)間短的優(yōu)點(diǎn)誓竿。主流垃圾收集器之一。
G1
采取標(biāo)記整理算法谈截,并行收集器筷屡。對(duì)比CMS的好處之一就是不會(huì)產(chǎn)生內(nèi)存碎片,此外簸喂,G1收集器不同于之前的收集器的一個(gè)重要特點(diǎn)是:G1回收的范圍是整個(gè)Java堆(包括新生代毙死,老年代),而前六種收集器回收的范圍僅限于新生代或老年代喻鳄。而且他的STW停頓時(shí)間是可以手動(dòng)控制一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段(可以用JVM參數(shù) -XX:MaxGCPauseMillis指定)扼倘,設(shè)置完后垃圾收集的時(shí)長(zhǎng)不得超過(guò)這個(gè)(近實(shí)時(shí))。
8除呵、詳細(xì)介紹一下 CMS 垃圾回收器再菊?
采取標(biāo)記清除算法,老年代并行收集器颜曾,號(hào)稱(chēng)以最短STW時(shí)間為目標(biāo)的收集器纠拔,并發(fā)高、停頓低泛豪、STW時(shí)間短的優(yōu)點(diǎn)稠诲。主流垃圾收集器之一。
主要分為四階段:
- 初始標(biāo)記:只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)的對(duì)象候址,速度很快吕粹,仍然需要暫停所有的工作線(xiàn)程。所以此階段會(huì)STW岗仑,但時(shí)間很短匹耕。
- 并發(fā)標(biāo)記:進(jìn)行 GC Roots 跟蹤的過(guò)程,和用戶(hù)線(xiàn)程一起工作荠雕,不需要暫停工作線(xiàn)程稳其。不會(huì)STW。
- 重新標(biāo)記:為了修正在并發(fā)標(biāo)記期間炸卑,因用戶(hù)程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄既鞠,仍然需要暫停所有的工作線(xiàn)程。STW時(shí)間會(huì)比第一階段稍微長(zhǎng)點(diǎn)盖文,但是遠(yuǎn)比并發(fā)標(biāo)記短嘱蛋,效率也很高。
- 并發(fā)清除:清除GC Roots不可達(dá)對(duì)象,和用戶(hù)線(xiàn)程一起工作洒敏,不需要暫停工作線(xiàn)程龄恋。
所以CMS的優(yōu)點(diǎn)是:
- 并發(fā)高
- 停頓低
- STW時(shí)間短。
缺點(diǎn):
對(duì)cpu資源非常敏感(并發(fā)階段雖然不會(huì)影響用戶(hù)線(xiàn)程凶伙,但是會(huì)一起占用CPU資源郭毕,競(jìng)爭(zhēng)激烈的話(huà)會(huì)導(dǎo)致程序變慢)。
無(wú)法處理浮動(dòng)垃圾函荣,當(dāng)剩余內(nèi)存不能滿(mǎn)足程序運(yùn)行要求時(shí)显押,系統(tǒng)將會(huì)出現(xiàn) Concurrent Mode Failure,失敗后而導(dǎo)致另一次Full GC的產(chǎn)生傻挂,由于CMS并發(fā)清除階段用戶(hù)線(xiàn)程還在運(yùn)行乘碑,伴隨程序的運(yùn)行自然會(huì)有新的垃圾產(chǎn)生,這一部分垃圾是出現(xiàn)在標(biāo)記過(guò)程之后的金拒,CMS無(wú)法在本次去處理他們蝉仇,所以只好留在下一次GC時(shí)候?qū)⑵淝謇淼簟?/p>
內(nèi)存碎片問(wèn)題(因?yàn)槭菢?biāo)記清除算法)。當(dāng)剩余內(nèi)存不能滿(mǎn)足程序運(yùn)行要求時(shí)殖蚕,系統(tǒng)將會(huì)出現(xiàn) Concurrent Mode Failure轿衔,臨時(shí) CMS 會(huì)采用 Serial Old 回收器進(jìn)行垃圾清除,此時(shí)的性能將會(huì)被降低睦疫。
9害驹、詳細(xì)介紹一下 G1 垃圾回收器?
采取標(biāo)記整理算法蛤育,并行收集器宛官。
特點(diǎn):
并行與并發(fā)執(zhí)行:利用多CPU的優(yōu)勢(shì)來(lái)縮短STW時(shí)間,在GC工作的時(shí)候瓦糕,用戶(hù)線(xiàn)程可以并行執(zhí)行底洗。
分代收集:無(wú)需其他收集器配合,自己G1會(huì)進(jìn)行分代收集咕娄。
空間整合:不會(huì)像CMS那樣產(chǎn)生內(nèi)存碎片亥揖。
可預(yù)測(cè)的停頓:可以手動(dòng)控制一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段(可以用JVM參數(shù) -XX:MaxGCPauseMillis指定),設(shè)置完后垃圾收集的時(shí)長(zhǎng)不得超過(guò)這個(gè)(近實(shí)時(shí))圣勒。
原理:
G1并不是簡(jiǎn)單的把堆內(nèi)存分為新生代和老年代兩部分费变,而是把整個(gè)堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),新生代和老年代也是一部分不需要連續(xù)Region的集合圣贸。G1跟蹤各個(gè)Region里面的垃圾堆積的價(jià)值大小挚歧,在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間吁峻,優(yōu)先回收價(jià)值最大的Region滑负。
補(bǔ)充:
Region不是孤立的在张,也就是說(shuō)一個(gè)對(duì)象分配在某個(gè)Region中,他并非只能被本Region中的其他對(duì)象引用矮慕,而是整個(gè)堆中任意的對(duì)象都可以相互引用瞧掺,那么在【可達(dá)性分析法】來(lái)判斷對(duì)象是否存活的時(shí)候也無(wú)需掃描整個(gè)堆,Region之間的對(duì)象引用以及其他手機(jī)其中新生代和老年代之間的對(duì)象引用虛擬機(jī)都是使用Remembered Set來(lái)避免全堆掃描的凡傅。
步驟:
- 初始標(biāo)記:僅僅標(biāo)記GCRoots能直接關(guān)聯(lián)到的對(duì)象,且修改TAMS的值讓下一階段用戶(hù)程序并發(fā)運(yùn)行時(shí)能正確可用的Region中創(chuàng)建的新對(duì)象肠缔。速度很快夏跷,會(huì)STW。
- 并發(fā)標(biāo)記:進(jìn)行 GC Roots 跟蹤的過(guò)程明未,和用戶(hù)線(xiàn)程一起工作槽华,不需要暫停工作線(xiàn)程。不會(huì)STW趟妥。
- 最終標(biāo)記:為了修正在并發(fā)標(biāo)記期間猫态,因用戶(hù)程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,仍然需要暫停所有的工作線(xiàn)程披摄。STW時(shí)間會(huì)比第一階段稍微長(zhǎng)點(diǎn)亲雪,但是遠(yuǎn)比并發(fā)標(biāo)記短,效率也很高疚膊。
- 篩選回收:首先對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序义辕,根據(jù)用戶(hù)所期望的GC停頓時(shí)間來(lái)制定回收計(jì)劃。
10寓盗、GC日志分析
11灌砖、Minor GC與Full GC分別在什么時(shí)候發(fā)生
新生代內(nèi)存(Eden區(qū))不夠用時(shí)候發(fā)生Minor GC也叫YGC。
Full GC發(fā)生情況:
- 老年代被寫(xiě)滿(mǎn)
- 持久代被寫(xiě)滿(mǎn)
- System.gc()被顯示調(diào)用(只是會(huì)告訴需要GC傀蚌,什么時(shí)候發(fā)生并不知道)
12基显、新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么區(qū)別善炫?
- 新生代回收器:Serial撩幽、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old箩艺、Parallel Old摸航、CMS
- 整堆回收器:G1
新生代垃圾回收器一般采用的是復(fù)制算法,復(fù)制算法的優(yōu)點(diǎn)是效率高舅桩,缺點(diǎn)是內(nèi)存利用率低纵穿;老年代回收器一般采用的是標(biāo)記-整理的算法進(jìn)行垃圾回收氏义。標(biāo)記整理很適合大對(duì)象,不會(huì)產(chǎn)生空間碎片。
13妈倔、棧上分配是什么意思
JVM允許將線(xiàn)程私有的對(duì)象分配在棧上楼熄,而不是分配在堆上。分配在棧上的好處是棧上分配不需要考慮垃圾回收,因?yàn)槌鰲5臅r(shí)候?qū)ο缶晚槑е黄鸪鋈チ伺偶啵瑳](méi)了,而不需要垃圾回收器的介入杰捂,從而提高系統(tǒng)性能舆床。
補(bǔ)充1:對(duì)象逃逸。
逃逸的目的是判斷對(duì)象的作用域是否有可能逃出函數(shù)體嫁佳。例如下面的代碼就顯示了一個(gè)逃逸的對(duì)象:
private User user;
private void hello(){
user = new User();
}
對(duì)象實(shí)例 user 是類(lèi)的成員變量挨队,可以被任何線(xiàn)程訪(fǎng)問(wèn),因此它屬于逃逸對(duì)象蒿往。但如果我們將代碼稍微改動(dòng)一下盛垦,該對(duì)象就可以線(xiàn)程非逃逸的了。
private void hello(){
User user = new User();
}
可以看到 user 實(shí)例作用域只在 hello 函數(shù)中瓤漏,不會(huì)被其他線(xiàn)程訪(fǎng)問(wèn)到腾夯,也不會(huì)訪(fǎng)問(wèn)。所以該 user 實(shí)例對(duì)象的作用域只在該函數(shù)中蔬充,因此它并未發(fā)生逃逸蝶俱。對(duì)于這樣的情況,虛擬機(jī)就有可能將其分配在棧上饥漫,而不在堆上跷乐。
補(bǔ)充2:TLAB,自行Google趾浅。
TLAB的全稱(chēng)是Thread Local Allocation Buffer愕提,即線(xiàn)程本地分配緩存區(qū),這是一個(gè)線(xiàn)程專(zhuān)用的內(nèi)存分配區(qū)域皿哨。
由于對(duì)象一般會(huì)分配在堆上浅侨,而堆是全局共享的。因此在同一時(shí)間证膨,可能會(huì)有多個(gè)線(xiàn)程在堆上申請(qǐng)空間如输。
因此,每次對(duì)象分配都必須要進(jìn)行同步(虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性)央勒,而在競(jìng)爭(zhēng)激烈的場(chǎng)合分配的效率又會(huì)進(jìn)一步下降不见。
JVM使用TLAB來(lái)避免多線(xiàn)程沖突,在給對(duì)象分配內(nèi)存時(shí)崔步,每個(gè)線(xiàn)程使用自己的TLAB稳吮,這樣可以避免線(xiàn)程同步,提高了對(duì)象分配的效率井濒。
-XX:+UseTLAB 使用TLAB
-XX:+TLABSize 設(shè)置TLAB大小
-XX:TLABRefillWasteFraction設(shè)置維護(hù)進(jìn)入TLAB空間的單個(gè)對(duì)象大小灶似,他是一個(gè)比例值列林,默認(rèn)為64,即如果對(duì)象大于整個(gè)空間的1/64酪惭,
-XX:TLABWasteTargetPercent設(shè)置TLAB空間所占用Eden空間的百分比大小 默認(rèn)是1%
則在堆創(chuàng)建對(duì)象
-XX:+PrintTLAB 查看TLAB信息
-XX:ResizeTLAB 自調(diào)整TLABRefillWasteFraction閾值希痴。
14、簡(jiǎn)述下對(duì)象的分配規(guī)則
對(duì)象優(yōu)先分配在Eden區(qū)春感,如果Eden區(qū)沒(méi)有足夠的空間時(shí)砌创,虛擬機(jī)執(zhí)行一次YGC。并將還活著的對(duì)象放到from/to區(qū)鲫懒,若本次YGC后還是沒(méi)有足夠的空間嫩实,則將啟用分配擔(dān)保機(jī)制在老年代中分配內(nèi)存。
大對(duì)象直接進(jìn)入老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)刀疙。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存)。
長(zhǎng)期存活的對(duì)象進(jìn)入老年代扫倡。虛擬機(jī)為每個(gè)對(duì)象定義了一個(gè)年齡計(jì)數(shù)器谦秧,如果對(duì)象經(jīng)過(guò)了1次YGC那么對(duì)象會(huì)進(jìn)入Survivor區(qū),之后每經(jīng)過(guò)一次YGC那么對(duì)象的年齡加1撵溃,直到達(dá)到閥值對(duì)象進(jìn)入老年區(qū)疚鲤。默認(rèn)閾值是15≡堤簦可以通過(guò)-XX:MaxTenuringThreshold參數(shù)來(lái)設(shè)置集歇。
動(dòng)態(tài)判斷對(duì)象的年齡。如果Survivor區(qū)中相同年齡的所有對(duì)象大小的總和大于Survivor空間的一半语淘,年齡大于或等于該年齡的對(duì)象可以直接進(jìn)入老年代诲宇。無(wú)需等到-XX:MaxTenuringThreshold參數(shù)要求的年齡。
動(dòng)態(tài)年齡判斷是有歧義的惶翻,要想面試加分姑蓝,必看這個(gè)
http://www.reibang.com/p/989d3b06a49d
空間分配擔(dān)保。每次進(jìn)行YGC時(shí)吕粗,JVM會(huì)計(jì)算Survivor區(qū)移至老年區(qū)的對(duì)象的平均大小纺荧,如果這個(gè)值大于老年區(qū)的剩余值大小則進(jìn)行一次Full GC,如果小于檢查HandlePromotionFailure設(shè)置颅筋,如果true則只進(jìn)行YGC宙暇,如果false則進(jìn)行Full GC。
JDK7
及以后這個(gè)參數(shù)就失效了议泵。
只要老年代的連續(xù)空間大于新生代對(duì)象的總大小或者歷次晉升到老年代的對(duì)象的平均大小就進(jìn)行MinorGC
占贫,否則FullGC
。
JDK7及以前這個(gè)參數(shù)的作用見(jiàn)下圖: