二、垃圾回收GC
在堆里面存放著java的所有對(duì)象實(shí)例,當(dāng)對(duì)象為“死去”,也就是不再使用的對(duì)象,就會(huì)進(jìn)行垃圾回收GC
1.如何判斷對(duì)象可以回收
1.1引用計(jì)數(shù)器
介紹
在對(duì)象中添加一個(gè)引用計(jì)數(shù)器,當(dāng)一個(gè)對(duì)象被其他變量引用時(shí)這個(gè)對(duì)象的引用計(jì)數(shù)器加1。當(dāng)某個(gè)變量不再引用這個(gè)對(duì)象時(shí)引用計(jì)數(shù)器減1。當(dāng)這個(gè)引用計(jì)數(shù)變?yōu)?時(shí),這個(gè)對(duì)象就會(huì)被垃圾回收。
優(yōu)點(diǎn)
判定效率很高
缺點(diǎn)
不完全準(zhǔn)確,當(dāng)兩個(gè)對(duì)象相互引用就判斷失效了。當(dāng)A引用B對(duì)象,B引用A對(duì)象,然后A和B不再被其他變量引用,垃圾不會(huì)回收,出現(xiàn)了垃圾回收失效。
1.2可達(dá)性分析算法(Java)
介紹
①要確定一個(gè)根對(duì)象GC Roots(肯定不會(huì)被垃圾回收的對(duì)象)作為起始節(jié)點(diǎn),當(dāng)垃圾回收前會(huì)對(duì)堆對(duì)象進(jìn)行掃描,判斷這些對(duì)象是否被根對(duì)象引用,如果沒有被引用那么這個(gè)對(duì)象就可以垃圾回收。
②java虛擬機(jī)是通過可達(dá)性分析判斷存活對(duì)象
哪些對(duì)象可以作為GC Root?
①虛擬機(jī)棧中引用的對(duì)象
各個(gè)線程調(diào)用的方法(參數(shù),局部變量)
②本地方法棧中native方法引用的對(duì)象
③方法區(qū)中類靜態(tài)屬性引用的對(duì)象
Java類的引用類型靜態(tài)變量
④方法區(qū)中常量引用的對(duì)象
字符串常量池StringTable里的引用
⑤所有被同步鎖synchronized持有的對(duì)象
⑥java虛擬機(jī)內(nèi)部的引用,核心類
1.3四種引用
1.強(qiáng)引用-不回收
介紹
程序代碼中普遍存在的引用賦值“Object obj = new Object()”這種引用關(guān)系。
特點(diǎn)
不回收,只要沿著GC Root引用鏈能夠找到這個(gè)對(duì)象,這個(gè)對(duì)象就不會(huì)被垃圾回收。
2.軟引用-內(nèi)存不足回收
介紹
軟引用(間接引用)是一些還有用但非必需的對(duì)象,當(dāng)被軟引用關(guān)聯(lián)的對(duì)象,系統(tǒng)發(fā)生內(nèi)存溢出錢,會(huì)把這些對(duì)象列進(jìn)回收范圍進(jìn)行二次回收。如果這次回收沒有足夠的內(nèi)存,會(huì)拋出內(nèi)存溢出異常
特點(diǎn)
當(dāng)垃圾回收后,此時(shí)內(nèi)存仍不夠,軟引用關(guān)聯(lián)的對(duì)象會(huì)進(jìn)行垃圾回收(內(nèi)存不夠才回收)
3.弱引用-發(fā)現(xiàn)就回收
介紹
弱引用是一些還有用但非必需的對(duì)象,生存到下一次垃圾回收為止。在系統(tǒng)進(jìn)行垃圾回收時(shí),發(fā)現(xiàn)弱引用,不管系統(tǒng)堆空間是否充足,都會(huì)回收軟引用關(guān)聯(lián)的對(duì)象。
特點(diǎn)
當(dāng)發(fā)生垃圾回收時(shí),弱引用關(guān)聯(lián)的對(duì)象都會(huì)被回收。(內(nèi)存不管夠不夠都回收)
4.虛引用-對(duì)象回收跟蹤
介紹
當(dāng)虛引用對(duì)象創(chuàng)建時(shí)會(huì)關(guān)聯(lián)一個(gè)引用隊(duì)列。虛引用在創(chuàng)建時(shí)必須提供引用隊(duì)列作為參數(shù)。當(dāng)垃圾回收準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)他是虛引用,在垃圾回收后把這個(gè)虛引用加入引用隊(duì)列,通知應(yīng)用程序?qū)ο蟮幕厥涨闆r。
5.終結(jié)器引用-引用隊(duì)列配合
介紹
用于實(shí)現(xiàn)對(duì)象的finalize()方法。當(dāng)終結(jié)器對(duì)象創(chuàng)建時(shí)會(huì)關(guān)聯(lián)一個(gè)引用隊(duì)列。當(dāng)準(zhǔn)備回收一個(gè)對(duì)象時(shí),發(fā)現(xiàn)是終結(jié)器引用,會(huì)把終結(jié)器引用對(duì)象放入引用隊(duì)列(此時(shí)對(duì)象沒回收)。由Finalizer線程調(diào)用該對(duì)象的finalize()方法,第二次垃圾回收就會(huì)回收此對(duì)象。
2.垃圾回收算法
2.1標(biāo)記清除
什么時(shí)候垃圾回收
掃描整個(gè)堆對(duì)象的過程中,如果發(fā)現(xiàn)對(duì)象被GC Root引用了,需要保留。如果這個(gè)對(duì)象沒有被GC Root引用,就要進(jìn)行垃圾回收。
標(biāo)記清除算法的兩個(gè)階段
①把沒有引用的對(duì)象標(biāo)記為垃圾。
②把垃圾對(duì)象占用的空間釋放,清除
③釋放不是空間清0,而是把起始地址記錄下來,然后將來把創(chuàng)建的新對(duì)象存儲(chǔ)這個(gè)位置
優(yōu)點(diǎn)
垃圾回收速度快
缺點(diǎn)
產(chǎn)生內(nèi)存碎片
2.2標(biāo)記整理
標(biāo)記整理兩個(gè)階段
①把沒有引用的對(duì)象標(biāo)記為垃圾
②把可用的對(duì)象向前移動(dòng),讓內(nèi)存更加緊湊。連續(xù)的空間變多。
優(yōu)點(diǎn)
沒有內(nèi)存碎片
缺點(diǎn)
對(duì)象整理過程中需要移動(dòng),垃圾回收效率低。
2.3復(fù)制
復(fù)制兩個(gè)階段
①把沒有引用的對(duì)象標(biāo)記為垃圾
②把活著的內(nèi)存分為兩塊from區(qū)和to區(qū)。把from區(qū)存活的對(duì)象復(fù)制到to區(qū)。然后清空from區(qū),然后交換from和to的位置
優(yōu)點(diǎn)
沒有內(nèi)存碎片
缺點(diǎn)
占用雙倍內(nèi)存空間
3.分代垃圾回收
介紹
垃圾回收時(shí),JVM結(jié)合3種算法協(xié)程工作。通過分代的垃圾回收機(jī)制,把堆內(nèi)存劃分為2塊新生代和老年代,新生代(對(duì)象用完丟棄)劃分為3個(gè)區(qū)域:伊甸園,幸存區(qū)From,幸存區(qū)To。
老年代(對(duì)象長(zhǎng)時(shí)間使用) 不同的區(qū)域,算法不同。
?
工作機(jī)制
①創(chuàng)建新的對(duì)象占用伊甸園的內(nèi)存空間,當(dāng)伊甸園內(nèi)存滿時(shí),觸發(fā)垃圾回收Minor GC。沿著GC Roots引用鏈去找,如果對(duì)象沒有被引用,就進(jìn)行垃圾標(biāo)記。然后采用復(fù)制的算法把伊甸園存活的對(duì)象復(fù)制到幸存區(qū)To中,并且對(duì)象壽命加1。然后交換from和to的位置。
?②當(dāng)伊甸園內(nèi)存空間再次溢出時(shí),觸發(fā)第二次垃圾回收Minor GC。沿著GC Roots根引用鏈進(jìn)行掃描伊甸園和幸存區(qū)。把存活的對(duì)象復(fù)制到幸存區(qū)To中,并且對(duì)象壽命加1。然后交換from和to的位置。
?③幸存區(qū)的對(duì)象壽命超過了閾值15,對(duì)象利用率高。晉升到老年代。(對(duì)象長(zhǎng)時(shí)間使用)
?④當(dāng)新生代和老年代內(nèi)存要溢出時(shí),觸發(fā)Full GC垃圾回收。
總結(jié)
①對(duì)象首先分配在伊甸園區(qū)域
②新生代空間不足時(shí),觸發(fā)Minor gc。伊甸園和from存活的對(duì)象,使用復(fù)制算法copy到to中。存活的對(duì)象年齡加1.交換from和to。
③minor gc會(huì)引發(fā)stop the world暫停其他用戶的線程。垃圾回收結(jié)束,用戶恢復(fù)運(yùn)行。
④當(dāng)對(duì)象壽命超過閾值時(shí),會(huì)晉升到老年代,最大壽命是15
⑤老年代空間不足,觸發(fā)Minor gc內(nèi)存仍不足,那么full gc.
相關(guān)JVM參數(shù)
4.垃圾回收器
4.1串行垃圾回收器
介紹
①單線程垃圾回收器
②堆內(nèi)存較小,適合個(gè)人電腦
③開啟串行垃圾回收器 -XX:+UseSerialGC = Serial + SerialOld
Serial工作新生代,復(fù)制算法
SerialOld 工作在老年代,標(biāo)記整理算法
工作流程
當(dāng)堆內(nèi)存要溢出時(shí),觸發(fā)垃圾回收。讓其他線程在安全點(diǎn)停止下來,等待垃圾回收線程的結(jié)束。
?
4.2吞吐量?jī)?yōu)先:并行
介紹
①多線程
②堆內(nèi)存較大,多核cpu支持
③讓單位時(shí)間內(nèi),STW時(shí)間最短
工作流程
多核CPU中,有4個(gè)線程運(yùn)行。當(dāng)堆內(nèi)存要溢出時(shí),觸發(fā)了垃圾回收。用戶線程在安全點(diǎn)停止。此時(shí)的垃圾回收器會(huì)開啟多個(gè)線程(跟CPU核數(shù)相關(guān))進(jìn)行垃圾回收。然后恢復(fù)其他線程的運(yùn)行。
相關(guān)參數(shù)
-XX:+UseParallelGC ~ -XX:+UseParallelOldGC ?開啟吞吐量?jī)?yōu)先垃圾回收器
-XX:+UseAdaptiveSizePolicy ?自適應(yīng)大小策略,新生代
-XX:GCTimeRatio=ratio ?調(diào)整吞吐量
-XX:MaxGCPauseMillis=ms 最大暫停毫秒數(shù)
-XX:ParallelGCThreads=n 運(yùn)行時(shí)線程數(shù)
4.3響應(yīng)時(shí)間優(yōu)先
介紹
①多線程
②堆內(nèi)存交大,多核cpu支持
③盡可能讓單次STW時(shí)間變短
相關(guān)參數(shù)
-XX:+UseConcMarkSweepGC~-XX:+UseParNewGC~SerialOld 開啟響應(yīng)時(shí)間優(yōu)先回收器
-XX:ParallelGCThreads=n~-XX:ConcGCThreads=threads 線程數(shù)
-XX:CMSInitiatingOccupancyFraction=percent 何時(shí)進(jìn)行垃圾回收
-XX:+CMSScavengeBeforeRemark 新生代垃圾回收
工作流程
當(dāng)老年代發(fā)生內(nèi)存泄漏,其他線程到達(dá)安全點(diǎn)暫停下來,垃圾回收器執(zhí)行初始標(biāo)記,用戶線程恢復(fù)運(yùn)行。垃圾回收線程進(jìn)行并發(fā)標(biāo)記,然后進(jìn)行重新標(biāo)記工作。垃圾回收線程進(jìn)行并發(fā)清理。
5.G1垃圾回收器
簡(jiǎn)介
Garbage First
JDK 9默認(rèn)
適用場(chǎng)景
①同時(shí)注重吞吐量和低延遲,默認(rèn)暫停目標(biāo)是200ms
②超大堆內(nèi)存,會(huì)將堆劃分多個(gè)大小相等的Region
③整體上是標(biāo)記+整理算法,兩個(gè)區(qū)域之間是復(fù)制算法。
相關(guān)JVM參數(shù)
-XX:+UseG1GC 開啟G1垃圾回收
-XX:G1HeapRegisonSize=size 設(shè)置劃分區(qū)域的大小
-XX:MaxGCPauseMills=time JVM最大暫停時(shí)間的目標(biāo)值
5.1 G1垃圾回收階段
1.Young Collection 新生代垃圾收集
①、G1會(huì)把堆內(nèi)存劃分成多個(gè)相等的區(qū)域。每個(gè)區(qū)域獨(dú)立作為伊甸園,幸存區(qū),老年代
②、新創(chuàng)建的對(duì)象會(huì)存儲(chǔ)在伊甸園,當(dāng)伊甸園內(nèi)存滿時(shí)會(huì)觸發(fā)新生代垃圾回收機(jī)制,會(huì)STW。
③、伊甸園存活的對(duì)象使用copy算法到幸存區(qū)。
④、當(dāng)幸存區(qū)的內(nèi)存溢出時(shí),觸發(fā)新生代垃圾回收。幸存區(qū)對(duì)象存活年齡超過一定時(shí)間會(huì)晉升到老年代
2. Young Collection+Concurrent Mark新生代垃圾收集+并發(fā)標(biāo)記
①在Young GC時(shí)進(jìn)行GC Root的初始標(biāo)記
②老年代占用堆空間比例達(dá)到閾值時(shí),進(jìn)行并發(fā)標(biāo)記(不會(huì)STW)
-XX:InitiatingHeapOccupancyPercent=percent(默認(rèn)45%)
3.Mixed Collection混合收集
介紹
會(huì)對(duì)E、S、O進(jìn)行全面垃圾回收
①最終標(biāo)記會(huì)STW
②拷貝存活會(huì)STW
-XX:MaxGCPauseMillis=ms
E:伊甸園區(qū)的存活對(duì)象通過復(fù)制算法到幸存區(qū)中
S:幸存區(qū)存活的對(duì)象復(fù)制到另一個(gè)幸存區(qū),符合晉升條件的到老年區(qū)
O:老年代區(qū)把存活對(duì)象復(fù)制到另一個(gè)老年代區(qū)
優(yōu)先收集垃圾最多的區(qū),目的是達(dá)到暫停時(shí)間段的目標(biāo)
5.2 Full GC?????
1.SerialGC
①新生代內(nèi)存不足發(fā)生的垃圾收集—minor gc
②老年代內(nèi)存不足發(fā)生的垃圾收集—full gc
2.ParallelGC
①新生代內(nèi)存不足發(fā)生的垃圾收集—minor gc
②老年代內(nèi)存不足發(fā)生的垃圾收集—full gc
3.CMS
①新生代內(nèi)存不足發(fā)生的垃圾收集—minor gc
②老年代內(nèi)存不足,并發(fā)失敗后才會(huì)full gc。
4.G1
①新生代內(nèi)存不足發(fā)生的垃圾收集—minor gc
②老年代內(nèi)存不足,老年代內(nèi)存跟堆內(nèi)存占比達(dá)到45%,觸發(fā)并發(fā)標(biāo)記和混合收集階段?;厥账俣?gt;垃圾產(chǎn)生速度,并發(fā)垃圾收集階段?;厥账俣?lt;垃圾產(chǎn)生速度,此時(shí)是full gc
5.3Young Collection跨代引用
1.新生代回收的跨代引用(老年代引用新生代)
①根對(duì)象有部分是來自老年代,老年代存活對(duì)象比較多,遍歷效率低。所以采用CardTable把老年代區(qū)域進(jìn)行細(xì)分成一個(gè)個(gè)的card。每個(gè)card是512k。如果老年代的對(duì)象引用了新生代的對(duì)象,就會(huì)把這個(gè)card標(biāo)記為臟卡。只關(guān)注臟卡,減少搜索范圍。
?②臟卡引用了新生代的對(duì)象。新生代通過Remembered Set知道對(duì)應(yīng)的臟卡。通過臟卡區(qū)域遍歷GC Roots
5.4 Remark-重新標(biāo)記
并發(fā)標(biāo)記階段的對(duì)象處理狀態(tài)
?
黑色已經(jīng)處理完成,存活的對(duì)象?;疑翘幚懋?dāng)中的。白色是尚未處理的垃圾。
在并發(fā)標(biāo)記階段可能出現(xiàn)B對(duì)C引用,接著斷開后C成為垃圾,然后A對(duì)C引用。此時(shí)C被判成垃圾了。
這個(gè)時(shí)候需要用到remark
Remark:對(duì)對(duì)象進(jìn)行進(jìn)一步檢查,當(dāng)對(duì)象引用發(fā)生改變時(shí),會(huì)給引用提供一個(gè)寫屏障(將C加入隊(duì)列),接著進(jìn)入重新標(biāo)記階段,對(duì)隊(duì)列對(duì)象進(jìn)行檢查,有強(qiáng)引用的不標(biāo)記為垃圾。
5.5 JDK8 u20字符串去重
指令
-XX:+UseStringDeduplication
規(guī)則
①將所有新分配的字符串放入一個(gè)隊(duì)列
②當(dāng)新生代回收時(shí),G1并發(fā)檢查是否有字符串重復(fù)
③如果值一樣,讓他們引用同一個(gè)char[]
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):節(jié)約大量?jī)?nèi)存
缺點(diǎn):多占用CPU時(shí)間,新時(shí)代回收時(shí)間略微增加
跟String.intern()不一樣
①String.intern()關(guān)注的是字符串對(duì)象
②字符串去重關(guān)注的是char[]
③JVM內(nèi)部使用不同的字符串表
5.6 JDK8 u40并發(fā)標(biāo)記類卸載
所有對(duì)象都經(jīng)過并發(fā)標(biāo)記后,知道哪些類不再使用。當(dāng)一個(gè)類加載器所有類不再使用,則卸載它所加載的所有類
-xx:+ClassUnloadingWithConcurrentMark默認(rèn)啟用
5.7 JDK8 u60 回收巨型對(duì)象
①一個(gè)對(duì)象大于region的一半時(shí),成為巨型對(duì)象。
②G1不會(huì)對(duì)巨型對(duì)象進(jìn)行拷貝
③回收時(shí)優(yōu)先考慮
④G1會(huì)跟蹤老年代所有incoming引用。這樣老年代incoming引用為0的巨型對(duì)象在新生代垃圾回收處理掉。
5.8 JDK9 并發(fā)標(biāo)記起始時(shí)間的調(diào)整
①并發(fā)標(biāo)記必須在堆空間占滿前完成,否則退化為FullGC
②JDK 9之前需要使用 -XX:InitiatingHeapOccupancyPercent
③JDK 9可以調(diào)整:
-XX:InitiatingHeapOccupancyPercent 用來設(shè)置初始值
進(jìn)行數(shù)據(jù)采樣并動(dòng)態(tài)調(diào)整文章來源:http://www.zghlxwxcb.cn/news/detail-696409.html
總會(huì)添加一個(gè)安全的空擋空間文章來源地址http://www.zghlxwxcb.cn/news/detail-696409.html
到了這里,關(guān)于02JVM_垃圾回收GC的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!