新聞中心
創(chuàng)新互聯(lián)www.cdcxhl.cn八線動(dòng)態(tài)BGP香港云服務(wù)器提供商,新人活動(dòng)買多久送多久,劃算不套路!
創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的商水網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!Java GC 機(jī)制與內(nèi)存分配策略詳解
收集算法是內(nèi)存回收的方法論,垃圾收集器是內(nèi)存回收的具體實(shí)現(xiàn)
自動(dòng)內(nèi)存管理解決的是:給對(duì)象分配內(nèi)存 以及 回收分配給對(duì)象的內(nèi)存
為什么我們要了解學(xué)習(xí) GC 與內(nèi)存分配呢?
在 JVM 自動(dòng)內(nèi)存管理機(jī)制的幫助下,不再需要為每一個(gè)new操作寫配對(duì)的delete/free代碼。但出現(xiàn)內(nèi)存泄漏和溢出的問題時(shí),如果不了解虛擬機(jī)是怎樣使用內(nèi)存的,那么排查錯(cuò)誤將是一項(xiàng)非常艱難的工作。
GC(垃圾收集器)在對(duì)堆進(jìn)行回收前,會(huì)先確定哪些對(duì)象“存活”,哪些已經(jīng)“死去”。那么就有了 對(duì)象存活判定算法 。
對(duì)象存活判定算法
引用計(jì)數(shù)算法:
算法思想:給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值加1,當(dāng)引用失效時(shí),計(jì)數(shù)器值減1,任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。
優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,判斷效率也很高
缺點(diǎn):很難解決對(duì)象之間相互循環(huán)引用的問題
可達(dá)性分析算法:
算法思想:通過一系列的“GC Roots”對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的。
如圖:object5、object6、object7雖然互相有關(guān)聯(lián),但是它們到GC Roots是不可達(dá)的,所以它們將會(huì)被判定為是可回收的對(duì)象。
可作為 GC Roots 的對(duì)象包括以下:
1.虛擬機(jī)棧中引用的對(duì)象
2.方法區(qū)中類靜態(tài)屬性引用的對(duì)象
3.方法區(qū)中常量引用的對(duì)象
4.本地方法棧中 JNI 引用的對(duì)象
生存還是死亡?
即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非是“非死不可”的,這時(shí)候他們暫時(shí)處于“緩刑”階段,要真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷兩次標(biāo)記過程:
如果對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈,那它將會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選的條件是此對(duì)象是否有必要執(zhí)行finalize()方法,當(dāng)對(duì)象沒有覆蓋finalize()方法,或者finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過,虛擬機(jī)將這兩種情況都視為“沒有必要執(zhí)行”。
如果這個(gè)對(duì)象被判定為有必要執(zhí)行finalize()方法,那么這個(gè)對(duì)象將會(huì)放置在一個(gè)叫做F-Queue的隊(duì)列中,并在稍后由一個(gè)由虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的Finalizer線程去執(zhí)行它,這里所謂的“執(zhí)行”是指虛擬機(jī)會(huì)觸發(fā)這個(gè)方法,但并不承諾會(huì)等待它運(yùn)行結(jié)束,這樣做的原因是:如果一個(gè)對(duì)象在finalize()方法中執(zhí)行緩慢,或者發(fā)生了死循環(huán),將很可能會(huì)導(dǎo)致F-Queue隊(duì)列中其他對(duì)象永久處于等待,甚至導(dǎo)致整個(gè)內(nèi)存回收系統(tǒng)的奔潰。
finalize()方法是對(duì)象逃脫死亡命運(yùn)的最后一次機(jī)會(huì),稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模的標(biāo)記,如果對(duì)象要在finalize()方法中成功拯救自己,只需重新與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)即可,比如把自己(this關(guān)鍵字)賦值給某個(gè)類變量或者對(duì)象的成員變量,那在第二次標(biāo)記時(shí)它將被移除出“即將回收“的集合,如果對(duì)象這時(shí)候還沒有逃脫,那基本上它就真的被回收了。
另外,任何一個(gè)對(duì)象的finalize()方法都只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次。finalize()能做的所有工作,使用try-finally或者其他方式都可以做的更好,更及時(shí),所以建議大家完全可以忘掉Java語(yǔ)言中有這個(gè)方法的存在。詳見《深入理解Java虛擬機(jī)》
垃圾收集算法
標(biāo)記-清除算法:
算法分為“標(biāo)記“和”清除“兩個(gè)階段:
首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象
不足之處:
1.效率問題,標(biāo)記和清除兩個(gè)過程的效率都不高。
2.空間問題:標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致以后在程序運(yùn)行過程中需要分配較大對(duì)象時(shí),無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作
復(fù)制算法:
算法實(shí)現(xiàn):將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。
這樣使得每次都是對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可。實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。算法的代價(jià)是講內(nèi)存縮小為了原來的一半,未免太高了點(diǎn)。
標(biāo)記-整理算法:
算法實(shí)現(xiàn):標(biāo)記出所有需要回收的對(duì)象、讓所有存活的對(duì)象都向一端移動(dòng)。然后直接清理掉端邊界以外的內(nèi)存。
分代收集算法:
算法實(shí)現(xiàn):根據(jù)對(duì)象存活周期的不同將內(nèi)存劃分為幾塊,一般是把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
在新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,那就選用復(fù)制算法。只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。
而老年代中因?yàn)閷?duì)象存活率高、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理“或者”標(biāo)記-整理“算法來進(jìn)行回收。
新生代 :分為三個(gè)空間,一個(gè)Eden空間 ,兩個(gè)Survivor空間。
絕大多數(shù)最新被創(chuàng)建的對(duì)象會(huì)被分配到這里,由于大部分對(duì)象在創(chuàng)建后會(huì)很快變得不可到達(dá),所以很多對(duì)象被創(chuàng)建在新生代,然后消失。對(duì)象從這個(gè)區(qū)域消失的過程我們稱之為”minor GC“。
老年代 :對(duì)象沒有變得不可達(dá),并且從新生代中存活下來,會(huì)被拷貝到這里。其所占用的空間要比新生代多。也正由于其相對(duì)較大的空間,發(fā)生在老年代上的GC要比新生代少得多。對(duì)象從老年代中消失的過程,我們稱之為”major GC“(或者”full GC“)
絕大多數(shù)剛剛被創(chuàng)建的對(duì)象會(huì)存放在Eden空間。在Eden空間執(zhí)行了第一次GC之后,存活的對(duì)象被移動(dòng)到其中一個(gè)Survivor空間。此后,在Eden空間執(zhí)行GC之后,存活的對(duì)象會(huì)被堆積在同一個(gè)Survivor空間。當(dāng)一個(gè)Survivor空間飽和,還在存活的對(duì)象會(huì)被移動(dòng)到另一個(gè)Survivor空間。之后會(huì)清空已經(jīng)飽和的那個(gè)Survivor空間。
在以上的步驟中重復(fù)幾次依然存活的對(duì)象,就會(huì)被移動(dòng)到老年代。
垃圾收集器
如圖是作用于不同分代的垃圾收集器,如果兩個(gè)收集器之間存在連線,就可以搭配使用。虛擬機(jī)所在的區(qū)域,則表示它是屬于新生代收集器還是老年代收集器。
學(xué)習(xí)各種垃圾收集器之前先了解下“Stop the World“。“Stop the World“會(huì)在任何一種GC算法中發(fā)生。“Stop the World“意味著 JVM 因?yàn)橐獔?zhí)行GC而停止了應(yīng)用程序的執(zhí)行。當(dāng)“Stop the World“發(fā)生時(shí),除了GC所需的線程以外,所有線程都處于等待狀態(tài),直到GC任務(wù)完成。GC優(yōu)化很多時(shí)候就是指減少“Stop the World“發(fā)生的時(shí)間。
“Stop the World“這樣理解很形象:你媽媽在給你打掃房間的時(shí)候,肯定也會(huì)讓你老老實(shí)實(shí)地在椅子上或者房間外等待著,如果她一邊打掃,你一邊亂扔紙屑,這房間還能打掃完?
Serial收集器:?jiǎn)尉€程,新生代收集器,使用復(fù)制算法。它只會(huì)使用一個(gè)CPU或一條收集線程去完成垃圾收集工作,在進(jìn)行垃圾收集時(shí),必須“Stop the World“,暫停替他所有的工作線程,直到它收集結(jié)束。
ParNew收集器:Serial收集器的多線程版本,控制參數(shù)、收集算法、Stop the World、對(duì)象分配規(guī)則、回收策略都與Serial收集器完全一樣
Parallel Scavenge收集器:生代收集器,使用復(fù)制算法,并行多線程。
Serial Old收集器:Serial收集器的老年代版本,單線程,使用標(biāo)記-整理算法。
Parallel Old收集器:Parallel Scavenge收集器的老年代版本,多線程,使用標(biāo)記-整理算法
CMS收集器:一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器,基于“標(biāo)記-清除”算法。運(yùn)作過程分四個(gè)步驟:初始標(biāo)記 、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除。
初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop the World”。初始標(biāo)記僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快。并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing 的過程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這一階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段稍長(zhǎng)一些,但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間段。
整個(gè)過程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過程,收集器線程都可以與用戶線程一起工作,所以,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。
優(yōu)點(diǎn):并發(fā)收集,低停頓
缺點(diǎn):對(duì)CPU資源非常敏感、無法處理浮動(dòng)垃圾、基于標(biāo)記清除算法,收集結(jié)束時(shí)有大量控件碎片產(chǎn)生
G1收集器:G1收集器是當(dāng)今收集器技術(shù)發(fā)展最前沿成果之一,一種面向服務(wù)端應(yīng)用的垃圾收集器。
G1的特點(diǎn):并行與并發(fā)、分代手機(jī)、空間整合、可預(yù)測(cè)的停頓
運(yùn)作過程如下:初始標(biāo)記、并發(fā)標(biāo)記、最終標(biāo)記、篩選回收。
初始標(biāo)記階段僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,并且修改TAMS的值,讓下一階段用戶程序并發(fā)運(yùn)行時(shí),能在正確可用的Region中創(chuàng)建新對(duì)象,這階段需要停頓線程,但耗時(shí)很短。
并發(fā)標(biāo)記階段是從GC Roots開始對(duì)堆中對(duì)象進(jìn)行可達(dá)性分析,找出存活的對(duì)象,這階段耗時(shí)較長(zhǎng),但可與用戶程序并發(fā)執(zhí)行。
而最終標(biāo)記階段則是則是為了修正在并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分標(biāo)記記錄,虛擬機(jī)將這段時(shí)間對(duì)象變化記錄在線程Remembered Set Logs里面。最終標(biāo)記階段需要把Remembered Set Logs的數(shù)據(jù)合并到Remembered Set中,這階段需要停頓線程,但是可并行執(zhí)行。
最后在篩選回收階段首先對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序。根據(jù)用戶所期望的GC停頓時(shí)間來制定回收計(jì)劃。
內(nèi)存分配與回收策略
對(duì)象優(yōu)先在Eden分配:大多數(shù)情況下,對(duì)象在新生代Eden區(qū)中分配。當(dāng)Eden區(qū)沒有足夠空間進(jìn)行分配時(shí),虛擬機(jī)將發(fā)起一次Minor GC。
大對(duì)象直接進(jìn)入老年代:大對(duì)象是指需要大量連續(xù)內(nèi)存控件的Java對(duì)象,最典型的大對(duì)象就是那種很長(zhǎng)的字符串以及數(shù)組。
長(zhǎng)期存活的對(duì)象將進(jìn)入老年代:虛擬機(jī)采用了分代收集的思想來管理內(nèi)存,那么內(nèi)存回收時(shí)就必須能識(shí)別哪些對(duì)象應(yīng)放在新生代,哪些對(duì)象應(yīng)放在老年代。為了做到這點(diǎn),虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡計(jì)數(shù)器。如果對(duì)象在Eden出生并經(jīng)過第一次Minor GC后仍然存活,并且能被Survivor容納的話,將被移動(dòng)到Survivor空間中,并且對(duì)象年齡設(shè)為1,對(duì)象在Survivor區(qū)中每“熬過”一次Minor GC,年齡就增加1歲,當(dāng)它的年齡增加到一定程度(默認(rèn)為15歲),就將會(huì)被晉升到老年代。
動(dòng)態(tài)對(duì)象年齡判定:虛擬機(jī)并不是永遠(yuǎn)要求對(duì)象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代,無須等到MaxTenuringThreshold中要求的年齡。
空間分配擔(dān)保:在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查老年代大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間,如果這個(gè)條件成立,那么Minor GC可以確保是安全的。如果不成立,則虛擬機(jī)會(huì)查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗。
總結(jié)
內(nèi)存回收與垃圾收集器在很多時(shí)候都是影響系統(tǒng)性能、并發(fā)能力的主要因素之一,虛擬機(jī)之所以提供多種不同的收集器以及提供大量的調(diào)節(jié)參數(shù),是因?yàn)橹挥懈鶕?jù)實(shí)際應(yīng)用需求、實(shí)現(xiàn)方式選擇最優(yōu)的收集方式才能獲取最高的性能。沒有固定收集器、參數(shù)組合,也沒有最優(yōu)的調(diào)優(yōu)方法,虛擬機(jī)也就沒有什么必然的內(nèi)存回收行為。
以上是我學(xué)習(xí)《深入理解Java虛擬機(jī)》一書的整理筆記。
參考資料《深入理解Java虛擬機(jī)》
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
網(wǎng)頁(yè)題目:JavaGC機(jī)制與內(nèi)存分配策略詳解-創(chuàng)新互聯(lián)
當(dāng)前網(wǎng)址:http://www.ef60e0e.cn/article/cspodc.html