內(nèi)存管理機(jī)制中講述了java運(yùn)行時(shí)區(qū)域的各個(gè)部分,其中程序計(jì)數(shù)器年碘、虛擬機(jī)棧澈歉、本地方法棧3個(gè)區(qū)域隨線程而生,隨線程而滅屿衅。而java堆和方法區(qū)則不一樣埃难,這個(gè)部分的內(nèi)存的分配和回收都是動態(tài)的,垃圾收集器所關(guān)注的是這部分的內(nèi)存涤久。在堆中涡尘,垃圾收集器的回收率比較高,尤其是新生代响迂,一次大約可以回收70%到95%的空間考抄。而方法區(qū)(永久代)的回收效率遠(yuǎn)低于此。
一蔗彤、怎么判斷對象是無用的對象川梅?垃圾收集器主要對被判定無用的對象進(jìn)行回收。有以下幾種算法:1然遏、引用計(jì)數(shù)算法贫途;2、可達(dá)性分析算法待侵。
1丢早、引用計(jì)數(shù)算法是當(dāng)一個(gè)地方引用它是計(jì)數(shù)器就加1,引用失效時(shí)計(jì)數(shù)器就減1秧倾,計(jì)數(shù)器為0時(shí)就是沒有被引用的對象怨酝。很多主流虛擬機(jī)沒有使用這個(gè)算法,因?yàn)樗茈y解決對象之間互相循環(huán)引用的問題那先;
2农猬、可達(dá)性分析算法的基本思路是通過一系列被稱為“GC Roots”的對象最為起點(diǎn),從這些節(jié)點(diǎn)開始向下搜索售淡,搜索走過的路徑稱為引用鏈盛险,當(dāng)一個(gè)對象到GC Roots沒有引用鏈時(shí)瞄摊,就判定對象無引用】嗑颍可是為GC Roots的對象有:虛擬機(jī)棧(棧幀中的本地變量表)中的引用對象换帜、方法區(qū)中的類靜態(tài)屬性引用的對象、方法區(qū)中常量池引用的對象鹤啡、本地方法棧中JNI(一般說的本地方法惯驼,即Native方法)引用的對象。
二递瑰、什么是引用祟牲?如果reference類型的數(shù)據(jù)中儲存的數(shù)據(jù)代表的另一塊內(nèi)存的地址,那這個(gè)數(shù)據(jù)就是一個(gè)引用抖部。JDK1.2之后對引用進(jìn)行了擴(kuò)展说贝,分為:? 1、強(qiáng)引用慎颗;2乡恕、軟引用3、弱引用俯萎;4傲宜、虛引用。
1夫啊、強(qiáng)引用就是引用還存在函卒,垃圾收集器永遠(yuǎn)不會回收掉被引用的對象;
2撇眯、軟引用是在系統(tǒng)將要發(fā)生內(nèi)存溢出前把這些對象列進(jìn)回收范圍之中進(jìn)行二次回收报嵌,如果內(nèi)存還是不足,才拋出內(nèi)存溢出異常熊榛,軟引用使用SoftReference類來實(shí)現(xiàn)沪蓬;
3、弱引用是對象只能活到下一次垃圾收集發(fā)生之前来候,無論內(nèi)存是否足夠,都會被回收掉逸雹。弱引用使用WeakReference類實(shí)現(xiàn)营搅;
4、虛引用完全不會對其生存時(shí)間構(gòu)成影響梆砸,也無法通過虛引用獲得對象實(shí)例转质。虛引用的唯一作用就是在實(shí)例被回收之前收到系統(tǒng)的通知。使用PhantomReference類來實(shí)現(xiàn)虛引用帖世。
三休蟹、對象的finalize()方法沸枯。對象在被判定沒有引用后和被垃圾回收之前會至少進(jìn)行2次標(biāo)記,第一次為可達(dá)性算法判定對象沒有引用鏈時(shí)赂弓,會對還有必要執(zhí)行finalize()方法的對象進(jìn)行標(biāo)記绑榴,當(dāng)對象沒有覆蓋finalize()方法或者該方法已經(jīng)被執(zhí)行過后,虛擬機(jī)將視為沒有必要執(zhí)行此方法盈魁。之后隊(duì)列被放進(jìn)一個(gè)F-Queue隊(duì)列等待執(zhí)行finalize()方法翔怎,然后GC將對對象進(jìn)行第二次標(biāo)記。
四杨耙、方法區(qū)(永久代)的回收內(nèi)容主要是兩部分:廢棄常量和無用的類赤套。廢棄常量:如常量池中的“abc”,如果沒有一個(gè)String對象叫做“abc”也沒有其他類使用“abc”珊膜,那這個(gè)常量就是廢棄常量容握。判定一個(gè)類是無用的類有三個(gè)必要條件:1、該類所有的實(shí)例都已經(jīng)被回收车柠,也就是java堆中不存在該類的實(shí)例剔氏;2、加載該類的ClassLoader已經(jīng)被回收堪遂;3介蛉、該類對java.lang.Class對象沒有在任何地方被引用,無法再任何地方通過反射訪問該類的方法溶褪。
五币旧、垃圾收集算法
1、標(biāo)記-清除算法? 是將被標(biāo)記為可回收的對象進(jìn)行清除猿妈,此算法有兩大問題:效率問題和空間問題吹菱。因?yàn)闃?biāo)記和清除兩個(gè)過程效率都不高,并且清除后的內(nèi)存空間不連續(xù)彭则;
2鳍刷、復(fù)制算法? 是將內(nèi)存分為兩塊,一塊滿時(shí)就將存活的對象按順序的復(fù)制到另一塊內(nèi)存中俯抖,然后將原有的類刪除输瓜。有點(diǎn)是實(shí)現(xiàn)簡單,運(yùn)行高效芬萍,但是可用內(nèi)存減小了一半尤揣。為此HotSpot虛擬機(jī)默認(rèn)Eden區(qū)和Survivor區(qū)的比例是8:1,以Eden區(qū)和一塊Suvivor區(qū)作為新生代柬祠,另一塊Suvivor區(qū)作為保留區(qū)域北戏,每當(dāng)垃圾回收時(shí)將存活的對象復(fù)制到保留區(qū)Suvivor中,清除新生代的所有對象漫蛔。如果存在對象100%存活的場景嗜愈,不能使用此算法
3旧蛾、標(biāo)記-整理算法? 標(biāo)記的過程和標(biāo)記-清除算法相同,然后讓所有的存活對象向一邊移動蠕嫁,然后將存活端邊的對象清除锨天。
4、分代收集算法? 只是把堆分成新生代和老年代拌阴,然后根據(jù)不同的代使用不同的回收算法绍绘。新生代一般使用復(fù)制算法,老年代則必須使用標(biāo)記-清除算法或者標(biāo)記-整理算法
六迟赃、收集器是收集算法的具體實(shí)現(xiàn)
1陪拘、Serial收集器(新生代、單線程纤壁、復(fù)制算法)? 是最基本左刽、發(fā)展歷史最悠久的收集器。它在JDK1.3之前是新生代的唯一選擇酌媒。這個(gè)收集器是一個(gè)單線程收集器欠痴,這里的單線程是指在收集器工作時(shí),必須暫停其他線程的工作秒咨,一般稱為Stop?The World喇辽。新生代采用復(fù)制算法,暫停所有線程雨席。
2菩咨、ParNew收集器(新生代、多線程陡厘、復(fù)制算法)? 其實(shí)就是Serial收集器的多線程版本抽米,出了使用多條線程進(jìn)行垃圾收集之外,其余行為包括Serial收集器可用所有控制參數(shù)(例如:-XX:SurvivorRatio糙置、-XX:PretenureSizeThreshold云茸、-XX:HandlePromotionFailure等)、收集算法谤饭、Stop The world标捺、對象分配規(guī)則、回收策略等都與Serial收集器一樣揉抵。使用-XX:+UseConcMarkSweepGC選項(xiàng)后默認(rèn)使用ParNew收集器亡容,也可以使用-XX:UseParNewGC來選擇使用」σǎ可以使用-XX:ParallelGCTreads來限制垃圾收集的線程數(shù)。
3身弊、parallel Scavenge收集器(新生代辟汰、多線程列敲、復(fù)制算法)? 它也是采用復(fù)制算法的收集器,看上去和ParNew都一樣帖汞,但是Parallel Scavenge收集器的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量戴而。所謂吞吐量,是CPU用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值翩蘸,即吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)所意。高吞吐量可盡快的運(yùn)行完用戶的代碼。Parallel Scavenge收集器提供了兩個(gè)準(zhǔn)確控制吞吐量的參數(shù)催首,分別是控制最大垃圾收集停頓時(shí)間的-XX:MaxGCPauseMills參數(shù)和直接設(shè)置垃圾收集時(shí)間和總時(shí)間的比例的-XX:GCTimeRatio扶踊。MaxGCPauseMills參數(shù)調(diào)的越大,垃圾收集次數(shù)越頻繁郎任,吞吐量就越小秧耗。打開-XX:+UseAdaptiveSizePolicy參數(shù)后,就不需要手工配置新生代大小舶治,Eden區(qū)和Suvivor區(qū)的比例等參數(shù)了分井。
4、Serail Old收集器(老年代霉猛,單線程尺锚、標(biāo)記-整理算法)??
5、Parallel Old收集器(老年代惜浅、多線程瘫辩、標(biāo)記-整理算法)?
6、CMS收集器(老年代赡矢、多線程并發(fā)杭朱、標(biāo)記清除算法)? 整個(gè)過程分為4個(gè)步驟:1、初始標(biāo)記(Stop The World)吹散;2弧械、并發(fā)標(biāo)記;3空民、重新標(biāo)記(Stop The World)刃唐;4、并發(fā)清除界轩。但是有三個(gè)缺點(diǎn):1画饥、對CPU資源敏感;2浊猾、無法處理浮動垃圾(在垃圾回收過程中產(chǎn)生的類)抖甘,JDK1.5之后老年代在使用了68%內(nèi)存后默認(rèn)開啟CMS收集器,可以通過-XX:CMSInitiatingOccupancyFranction參數(shù)來設(shè)置觸發(fā)半分比葫慎;3衔彻、標(biāo)記清除算法帶來的空間使用不充分薇宠,可以開啟-XX:+UseCMSCompactAtFullColection參數(shù)在觸發(fā)Full GC前整合碎片空間,但是停頓時(shí)間會相應(yīng)變長艰额。另一個(gè)參數(shù)-XX:CMSFullGCCsBeforeCompaction是設(shè)置多少次Full GC后必須出現(xiàn)一次整合枉侧。
7波闹、G1收集器? 結(jié)合了以上收集器的特點(diǎn),并行并發(fā),分代收集(G1收集器能獨(dú)立管理整個(gè)GC堆)掸宛,空間整合(G1收集器從整體上看是基于標(biāo)記-整理算法實(shí)現(xiàn)的贴见,從局部上看是基于復(fù)制算法實(shí)現(xiàn)的)佑力,可預(yù)測的停頓筋量。
六、內(nèi)存分配
1棕硫、對象優(yōu)先在Eden分配髓涯,當(dāng)Eden區(qū)內(nèi)存不足時(shí)會發(fā)生一次Minor GC,可通過-XX:+PrintGCDetails打印日志哈扮。
2纬纪、大對象直接進(jìn)入老年代,需要連續(xù)內(nèi)存空間的Java對象比如很長的字符串和數(shù)組滑肉,程序應(yīng)該避免這種寫法包各。可以通過設(shè)置-XX:PertenureSizeThreshold參數(shù)來判斷內(nèi)存大于多少的對象直接被放到老年代靶庙。
3问畅、長期存活的對象將進(jìn)入老年代,在Eden區(qū)中經(jīng)過一側(cè)Minor GC后并且成功進(jìn)入到Survivor區(qū)的對象年齡計(jì)數(shù)器加1六荒,并且在之后的Minor
GC發(fā)生后繼續(xù)累加护姆,累加到閾值后進(jìn)入老年代,可以通過-XX:MaxTenuringThreshold參數(shù)設(shè)置這個(gè)閾值掏击。
4卵皂、動態(tài)對象年齡判定是指虛擬機(jī)并不是永遠(yuǎn)的要求對象年齡必須達(dá)到閾值后才能被轉(zhuǎn)移到老年代,如果Survivor區(qū)中相同年齡所有對象大小的總和大于Survivor空間的一半砚亭,年齡大于等于該年齡的對象直接被轉(zhuǎn)移到老年代灯变。
5、空間分配擔(dān)保是指在發(fā)生Minor GC之前虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間捅膘,如果這個(gè)條件成立添祸,那么Minor GC可以確保是安全的。如果不成立寻仗,則虛擬機(jī)會查看HandelPromotionFailure設(shè)置值是否允許擔(dān)保失敗刃泌,如果允許。那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次轉(zhuǎn)移到老年代對象的平均大小,如果大于耙替,則嘗試一次Minor GC鲤遥,這次GC會有風(fēng)險(xiǎn)。如果小于就進(jìn)行一次FULL GC(清理整個(gè)堆)林艘。