寫在前面:為了更加深入的了解java虛擬機(jī)榨汤,就看了一下《深入理解java虛擬機(jī)》這本書惶楼,一方面為了總結(jié)一下自己的認(rèn)識(shí),另一方面就是想與各位分享滔以,如果有什么不對的地方捉腥,歡迎指正
深入理解java虛擬機(jī)(一)java內(nèi)存區(qū)域與內(nèi)存溢出異常
垃圾收集器與內(nèi)存分配策略
垃圾收集,三個(gè)步驟
什么時(shí)候收集你画,收集那些抵碟,怎么收集
1、收集那些
我們會(huì)將一些不使用的對象進(jìn)行收集坏匪,進(jìn)行回收內(nèi)存空間拟逮,我們怎么知道呢
1、引用計(jì)數(shù)法
如果這個(gè)實(shí)例被其他地方引用适滓,那么計(jì)數(shù)器加一敦迄,如果解除引用那么就減一,當(dāng)計(jì)數(shù)器為0說明沒有地方使用凭迹,即可回收罚屋,但是缺點(diǎn)就是如果兩個(gè)對象互相引用,但是后續(xù)又用不到嗅绸,那么就不會(huì)被回收脾猛,內(nèi)存浪費(fèi)
2、可達(dá)性分析
通過GC Root向下尋找朽砰,形成一條引用鏈尖滚,如果對象不再這條鏈上,那么說明該對象不可達(dá)瞧柔,可回收漆弄,
什么是GC Root
- 虛擬機(jī)棧中引用的對象
- 方法區(qū)中靜態(tài)屬性引用的對象
- 方法區(qū)常亮引用的對象
- 本地方法棧中引用的對象
3、引用
我們不論是使用引用計(jì)數(shù)法造锅,還是使用可達(dá)性分析撼唾,都有引用這個(gè)詞,什么意思呢
jdk1.2之前的引用哥蔚,是reference類型的數(shù)據(jù)存儲(chǔ)的是另一塊內(nèi)存的起始地址倒谷,那么就稱這塊內(nèi)存代表著一個(gè)引用蛛蒙,這種情況下,一個(gè)對象只有被引用和沒有被引用兩種狀態(tài)渤愁,
我們希望有一些對象牵祟,在內(nèi)存空間足夠的時(shí)候,能夠保留在內(nèi)存中抖格,如果內(nèi)存空間在進(jìn)行垃圾收集之后還是很緊張诺苹,那么就拋棄這些對象,所以出現(xiàn) 強(qiáng)引用(Strong Reference)雹拄,軟引用(Soft Reference)收奔,弱引用(Weak Reference),虛引用(Phantom Reference)滓玖,這4中引用強(qiáng)度依次減弱
- 強(qiáng)引用
強(qiáng)引用就是指程序代碼中普遍存在的 Object obj = new Object()這類引用坪哄,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會(huì)回收掉被引用的對象势篡,使用StrongReference實(shí)現(xiàn) - 軟引用
jdk1.2之后出現(xiàn)軟引用用來描述一些有用翩肌,但不是必須的對象,軟引用關(guān)聯(lián)的對象殊霞,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前摧阅,將會(huì)把這些對象列入可回收范圍中進(jìn)行第二次回收,如果這次回收還不能解決內(nèi)存問題绷蹲,那么拋出內(nèi)存溢出錯(cuò)誤,顾孽,使用SoftReference實(shí)現(xiàn) - 弱引用
弱引用也是用來描述非必需的對象祝钢,被弱引用關(guān)聯(lián)的對象,只能生存到下一次垃圾收集發(fā)生之前若厚,當(dāng)垃圾收集器工作時(shí)候拦英,無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉测秸,使用WeakReference類實(shí)現(xiàn)虛引用 - 虛引用
最弱的引用疤估,一個(gè)對象是否有徐勇勇的存在,完全不會(huì)對其生存時(shí)間構(gòu)成影響霎冯,也無法通過虛引用來取得一個(gè)對象實(shí)例铃拇,為一個(gè)對象設(shè)置虛引用關(guān)聯(lián)的唯一目的,就是能在這個(gè)對象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知沈撞,jdk1.2之后通過PhantomReference類來實(shí)現(xiàn)虛引用慷荔,
4.對象死了嗎
當(dāng)對象沒有存在GC Root引用鏈上,也不是馬上死缠俺,會(huì)被進(jìn)行一次標(biāo)記并進(jìn)行一次篩選显晶,查看對象的finalliaze方法是否被覆蓋贷岸,或者是否被虛擬機(jī)調(diào)用過了,
如果被覆蓋或者沒有被調(diào)用磷雇,那么這個(gè)對象會(huì)被放置到F-Queue中偿警,等待執(zhí)行finaliaze方法,虛擬機(jī)會(huì)開啟一條低優(yōu)先級(jí)的線程去觸發(fā)這些方法唯笙,如果對象在finalize方法中重新加入了引用鏈(把自己賦值給某個(gè)類變量螟蒸,或者對象的成員變量),那么就不會(huì)被回收了睁本,
5.回收方法區(qū)
回收方法區(qū)內(nèi)廢棄常量和無用的類
廢棄常量:如果常量池中有各“abc”的字面量尿庐,但是卻沒有一個(gè)string 對象叫做abc,那么這個(gè)就是廢棄常量呢堰,當(dāng)垃圾回收的時(shí)候就會(huì)回收
無用的類:在堆中已經(jīng)沒有這個(gè)類的實(shí)例抄瑟,加載該類的類加載器已經(jīng)被回收,這個(gè)Class對象沒有在任何地方被引用枉疼,無法通過反射獲取這個(gè)類的方法
2皮假、怎么收集
垃圾收集在方法區(qū)和堆區(qū)
1.標(biāo)記-清除算法
標(biāo)記清除:當(dāng)堆內(nèi)存耗盡的時(shí)候,觸發(fā)GC線程骂维,使用戶線程停止惹资,遍歷所有的GC root,將還存活的隨想標(biāo)記一下航闺,之后清除所有未標(biāo)記的對象褪测,繼續(xù)用戶線程,缺點(diǎn):產(chǎn)生內(nèi)存碎片潦刃,使得內(nèi)存不連續(xù)侮措,當(dāng)有對象申請大內(nèi)存的時(shí)候,出現(xiàn)問題
復(fù)制算法: 將原內(nèi)存一分為二乖杠,只用一半的內(nèi)存分扎,當(dāng)需要垃圾回收的時(shí)候, 標(biāo)記存活對象胧洒,然后將存活對象拷貝到另一半空間中去畏吓,前一半直接全部回收,缺點(diǎn):無論什么時(shí)候卫漫,只有一半的內(nèi)存可用菲饼,浪費(fèi),拷貝對象需要時(shí)間
標(biāo)記整理算法:標(biāo)記存活對象汛兜,將存活對象按照某種規(guī)則排列巴粪,清除別的對象,缺點(diǎn):需要遍歷標(biāo)記,需要移動(dòng)對象肛根,浪費(fèi)時(shí)間
分代回收:
堆分為新生代和老年代辫塌,新生代分三個(gè)模塊,Enden surviver0 surviver1 8:1:1 new出來的對象放在enden區(qū)域派哲,當(dāng)發(fā)生gc臼氨,將存活的放在surviver0,第二次將存活的芭届,surviver0中的放在surviver1中储矩,反復(fù)如此,使用標(biāo)記復(fù)制算法褂乍,當(dāng)新生代的對象活過一定次數(shù)gc持隧,就會(huì)去到老年代
新生代使用復(fù)制算法,老年代使用標(biāo)記清除逃片,標(biāo)記整理算法
3屡拨、常見的垃圾收集器
1.Serial垃圾收集器(客戶端模式下的虛擬機(jī),新生代默認(rèn)收集器)
單線程垃圾收集器褥实,在進(jìn)行垃圾收集的時(shí)候呀狼,會(huì)停止用戶線程,當(dāng)垃圾收集完畢损离,開始用戶線程哥艇,這個(gè)是對用戶不可見的,完全由虛擬機(jī)執(zhí)行僻澎,單CPU效率最高的收集器貌踏,沒有線程交替的開銷
2.ParNew垃圾收集器(服務(wù)端模式下的虛擬機(jī),新生代默認(rèn)收集器)
Serial收集器的多線程版本窟勃,在進(jìn)行垃圾收集哩俭,也會(huì)先停止用戶線程,除了Serial只有這個(gè)收集器可以和CMS收集器使用拳恋,
3. Parallel Scavenge收集器
多線程并行的垃圾收集器,與ParNew類似砸捏,但是這個(gè)收集器注點(diǎn)在于達(dá)到一個(gè)可控制的吞吐量谬运,吞吐量就是CPU用于運(yùn)行用戶代碼的時(shí)間與cpu總消耗時(shí)間的比值,
4. Serial Old收集器
是serial收集器的老年版本垦藏,同樣是一個(gè)單線程收集器梆暖,使用標(biāo)記整理算法,是給客戶端模式下的虛擬機(jī)使用掂骏,在server模式下的客戶端有兩大用途:一是在jdk1.5之前轰驳,與Parallel Scavenge收集器搭配使用,二就是作為CMS收集器的后備預(yù)案,在并發(fā)收集器發(fā)生Concurrent Mode Failure時(shí)使用级解,
也會(huì)暫停用戶線程
5. CMS收集器
是一種以獲取短回收停頓時(shí)間為目標(biāo)的收集器冒黑,采用標(biāo)記清除方式,運(yùn)作過程有
- 初始標(biāo)記
- 并發(fā)標(biāo)記
- 重新標(biāo)記
- 并發(fā)清除
其中初始標(biāo)記與重新標(biāo)記需要停止用戶線程勤哗,初始標(biāo)記只是標(biāo)記一下GC Root能直接關(guān)聯(lián)到的對象抡爹,速度很快,并發(fā)標(biāo)記階段就是進(jìn)行GC Root Tracing的過程芒划,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)作而導(dǎo)致的標(biāo)記產(chǎn)生變動(dòng)的那一部分對象的記錄冬竟,這個(gè)階段停頓的時(shí)間一般會(huì)比初始標(biāo)記階段稍長一點(diǎn),但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間短民逼,泵殴,由于整個(gè)過程中耗時(shí)最長的并發(fā)標(biāo)記和并發(fā)清除過程,收集器線程可以和用戶線程一起工作拼苍,所以笑诅,總體來看,CMS收集器的內(nèi)存回收過程食欲用戶線程一起并發(fā)執(zhí)行的映屋,
缺點(diǎn):CMS無法處理浮動(dòng)垃圾苟鸯,可能出現(xiàn)Concurrent Mode Failure失敗而導(dǎo)致另一次Full GC的產(chǎn)生,由于CMS的并發(fā)清理階段適合用戶線程一起的棚点,那么程序運(yùn)行就會(huì)有新的垃圾產(chǎn)生早处,這一部分垃圾在標(biāo)記之后,所以CMS不會(huì)處理瘫析,下一次處理砌梆, 也是由于在垃圾收集階段用戶線程還需要運(yùn)行,那也就是需要預(yù)留有足夠的內(nèi)存空間給用戶線程使用贬循,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全填滿在進(jìn)行收集咸包,需要留一部分空間提供并發(fā)收集時(shí)的程序運(yùn)作使用,當(dāng)在CMS運(yùn)行期間預(yù)留的內(nèi)存無法滿足程序需要就會(huì)出現(xiàn)一次Concurrent Mode Failure失敗杖虾,這個(gè)時(shí)候就會(huì)啟動(dòng)Serial Old收集器進(jìn)行老年代的垃圾收集烂瘫,
6. G1收集器
具有以下特點(diǎn),
- 并行與并發(fā)
充分利用多核多CPU奇适,縮短Stop The World的時(shí)間坟比,G1收集器通過并發(fā)的方式讓java程序繼續(xù)執(zhí)行 - 分代收集
與其他收集器一樣,分代概念也在G1中保存嚷往,G1不需要通過其他收集器配合就可以管理真?zhèn)€堆葛账,但是它可以通過不同的方式處理, - 空間整合
G1從整體來看是基于標(biāo)記整理算法實(shí)現(xiàn)的皮仁,從局部來看是基于復(fù)制算法實(shí)現(xiàn)的籍琳,不會(huì)產(chǎn)生內(nèi)存碎片菲宴,收集后能提供規(guī)整的可用內(nèi)存,不會(huì)因?yàn)榉峙浯髮ο笳也坏竭B續(xù)內(nèi)存空間觸發(fā)GC - 可預(yù)測的停頓
G1可以讓使用者明確指定在一個(gè)長度為n毫秒的時(shí)間片段內(nèi)趋急,消耗在垃圾收集上的時(shí)間不能超過n毫秒喝峦,
具體過程
G1將對分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),雖然保存著新生代與老年代的概念宣谈,但是不是物理隔離的愈犹,G1可以預(yù)測停頓時(shí)間,是因?yàn)槲懦螅谶M(jìn)行垃圾收集的時(shí)候漩怎,G1跟蹤各個(gè)Region里面的垃圾堆積的價(jià)值大小(回收所獲得的空間大小與回收所需要的時(shí)間的經(jīng)驗(yàn)值)嗦嗡,在后臺(tái)維護(hù)一個(gè)列表勋锤,每次根據(jù)允許的收集時(shí)間,優(yōu)先回收價(jià)值最大的Region侥祭,保證了G1收集器在有限時(shí)間內(nèi)有高效率
具體步驟:
- 初始標(biāo)記
- 并發(fā)標(biāo)記
- 最終標(biāo)記
- 篩選回收
初始標(biāo)記階段是標(biāo)記一下GC root能夠達(dá)到的對象叁执,會(huì)停頓用戶線程,但是時(shí)間很短矮冬,并發(fā)標(biāo)記是從GC Root開始從堆中對象進(jìn)行可達(dá)性分析谈宛,找出存活的對象,時(shí)間較長胎署,但是可以和用戶線程并發(fā)執(zhí)行吆录,最終標(biāo)記階段是為了修正在并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分,篩選回收琼牧,則是進(jìn)行回收
4恢筝、內(nèi)存分配與回收策略
Minor GC : 新生代的垃圾回收
Full GC:老年代的垃圾回收,也會(huì)回收新生代
1.對象優(yōu)先在Eden分配
大多數(shù)情況下巨坊,對象在新生代Eden區(qū)中分配撬槽,當(dāng)Eden沒有足夠空間分配,就是觸發(fā)Minor GC
2.大對象直接進(jìn)入老年代
大對象趾撵,指需要大量連續(xù)內(nèi)存空間的java對象侄柔,最典型的的就是那種很長的字符串和數(shù)組
3.長期存活的對象進(jìn)入老年代
在Eden區(qū)的對象經(jīng)過一次Minor GC,會(huì)被移動(dòng)到Survivor區(qū)占调,年齡加一勋拟,當(dāng)存活過一定時(shí)間,就會(huì)進(jìn)入老年區(qū)
4.動(dòng)態(tài)對象年齡判定
當(dāng)survivor中相同年齡所有對象大小的總和大于survivor空間的一般妈候,年齡大于等于改年齡的對象可以直接進(jìn)入老年代,不需要達(dá)到指定年齡
5.空間分配擔(dān)保
在進(jìn)行Minor GC之前挂滓,虛擬機(jī)會(huì)檢查老年代的最大可用的連續(xù)空間是否大于新生區(qū)所有對象的總空間苦银,如果這個(gè)條件成立,那么這次Minor GC是安全的,如果不成立幔虏,虛擬機(jī)會(huì)查看HandlerPromotionFailure設(shè)置值是否允許擔(dān)保失敗纺念,如果允許那么會(huì)繼續(xù)檢查老年代最大可用連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于想括,就嘗試著進(jìn)行一次Minor GC陷谱,盡管這次Minor GC是有風(fēng)險(xiǎn)的,如果小于瑟蜈,或者不允許冒險(xiǎn)烟逊,那么就進(jìn)行Full GC
更多內(nèi)容請看后續(xù)
QQ群:552113611