摘要:它的基本原理是,在每個(gè)對(duì)象中保存該對(duì)象的引用計(jì)數(shù),當(dāng)引用發(fā)生增減時(shí)對(duì)計(jì)數(shù)進(jìn)行更新。實(shí)現(xiàn)容易是引用計(jì)數(shù)算法最大的優(yōu)點(diǎn)。引用計(jì)數(shù)最大的缺點(diǎn),就是無(wú)法釋放循環(huán)引用的對(duì)象。為了避免這種情況的發(fā)生,對(duì)引用計(jì)數(shù)的操作必須采用獨(dú)占的方式來(lái)進(jìn)行。
jvm系列
垃圾回收基礎(chǔ)
JVM的編譯策略
GC的三大基礎(chǔ)算法
GC的三大高級(jí)算法
GC策略的評(píng)價(jià)指標(biāo)
JVM信息查看
GC通用日志解讀
jvm的card table數(shù)據(jù)結(jié)構(gòu)
Java類初始化順序
Java對(duì)象結(jié)構(gòu)及大小計(jì)算
Java的類加載機(jī)制
Java對(duì)象分配簡(jiǎn)要流程
年老代過(guò)大有什么影響
Survivor空間溢出實(shí)例
關(guān)于Object=null
Java線程與Xss
基本術(shù)語(yǔ) 1. 垃圾(Garbage)就是需要回收的對(duì)象。
作為編寫程序的人,是可以做出“這個(gè)對(duì)象已經(jīng)不再需要了”這樣的判斷,但計(jì)算機(jī)是做不到的。因此,如果程序(通過(guò)某個(gè)變量等等)可能會(huì)直接或間接地引用一個(gè)對(duì)象,那么這個(gè)對(duì)象就被視為“存活”;與之相反,已經(jīng)引用不到的對(duì)象被視為“死亡”。將這些“死亡”對(duì)象找出來(lái),然后作為垃圾進(jìn)行回收,這就是GC的本質(zhì)。
2、根(Root)就是判斷對(duì)象是否可被引用的起始點(diǎn)。
至于哪里才是根,不同的語(yǔ)言和編譯器都有不同的規(guī)定,但基本上是將變量和運(yùn)行棧空間作為根。好了,用上面這兩個(gè)術(shù)語(yǔ),我們來(lái)講一講主要的GC算法。
三大基礎(chǔ)GC算法 1、標(biāo)記清除法/標(biāo)記壓縮法標(biāo)記清除(Mark and Sweep)是最早開發(fā)出的GC算法(1960年)。它的原理非常簡(jiǎn)單,首先從根開始將可能被引用的對(duì)象用遞歸的方式進(jìn)行標(biāo)記,然后將沒(méi)有標(biāo)記到的對(duì)象作為垃圾進(jìn)行回收。
圖1顯示了標(biāo)記清除算法的大致原理。圖1中的(1)部分顯示了隨著程序的運(yùn)行而分配出一些對(duì)象的狀態(tài),一個(gè)對(duì)象可以對(duì)其他的對(duì)象進(jìn)行引用。圖中(2)部分中,GC開始執(zhí)行,從根開始對(duì)可能被引用的對(duì)象打上“標(biāo)記”。大多數(shù)情況下,這種標(biāo)記是通過(guò)對(duì)象內(nèi)部的標(biāo)志(Flag)來(lái)實(shí)現(xiàn)的。于是,被標(biāo)記的對(duì)象我們把它們涂黑。圖中(3)部分中,被標(biāo)記的對(duì)象所能夠引用的對(duì)象也被打上標(biāo)記。重復(fù)這一步驟的話,就可以將從根開始可能被間接引用到的對(duì)象全部打上標(biāo)記。到此為止的操作,稱為標(biāo)記階段(Mark phase)。
標(biāo)記階段完成時(shí),被標(biāo)記的對(duì)象就被視為“存活”對(duì)象。圖1中的(4)部分中,將全部對(duì)象按順序掃描一遍,將沒(méi)有被標(biāo)記的對(duì)象進(jìn)行回收。這一操作被稱為清除階段(Sweep phase)。
在掃描的同時(shí),還需要將存活對(duì)象的標(biāo)記清除掉,以便為下一次GC操作做好準(zhǔn)備。標(biāo)記清除算法的處理時(shí)間,是和存活對(duì)象數(shù)與對(duì)象總數(shù)的總和相關(guān)的。
作為標(biāo)記清除的變形,還有一種叫做標(biāo)記壓縮(Mark and Compact)的算法,它不是將被標(biāo)記的對(duì)象清除,而是將它們不斷壓縮。
2、復(fù)制收集算法標(biāo)記清除算法有一個(gè)缺點(diǎn),就是在分配了大量對(duì)象,并且其中只有一小部分存活的情況下,所消耗的時(shí)間會(huì)大大超過(guò)必要的值,這是因?yàn)樵谇宄A段還需要對(duì)大量死亡對(duì)象進(jìn)行掃描。復(fù)制收集(Copy and Collection)則試圖克服這一缺點(diǎn)。在這種算法中,會(huì)將從根開始被引用的對(duì)象復(fù)制到另外的空間中,然后,再將復(fù)制的對(duì)象所能夠引用的對(duì)象用遞歸的方式不斷復(fù)制下去。
圖2的(1)部分是GC開始前的內(nèi)存狀態(tài),這和圖1的(1)部分是一樣的。圖2的(2)部分中,在舊對(duì)象所在的“舊空間”以外,再準(zhǔn)備出一塊“新空間”,并將可能從根被引用的對(duì)象復(fù)制到新空間中。圖中(3)部分中,從已經(jīng)復(fù)制的對(duì)象開始,再將可以被引用的對(duì)象像一串糖葫蘆一樣復(fù)制到新空間中。復(fù)制完成之后,“死亡”對(duì)象就被留在了舊空間中。圖中(4)部分中,將舊空間廢棄掉,就可以將死亡對(duì)象所占用的空間一口氣全部釋放出來(lái),而沒(méi)有必要再次掃描每個(gè)對(duì)象。下次GC的時(shí)候,現(xiàn)在的新空間也就變成了將來(lái)的舊空間。
通過(guò)圖2我們可以發(fā)現(xiàn),復(fù)制收集方式中,只存在相當(dāng)于標(biāo)記清除方式中的標(biāo)記階段。由于清除階段中需要對(duì)現(xiàn)存的所有對(duì)象進(jìn)行掃描,在存在大量對(duì)象,且其中大部分都即將死亡的情況下,全部掃描一遍的開銷實(shí)在是不小。而在復(fù)制收集方式中,就不存在這樣的開銷。
但是,和標(biāo)記相比,將對(duì)象復(fù)制一份所需要的開銷則比較大,因此在“存活”對(duì)象比例較高的情況下,反而會(huì)比較不利。這種算法的另一個(gè)好處是它具有局部性(Lo-cality)。在復(fù)制收集過(guò)程中,會(huì)按照對(duì)象被引用的順序?qū)?duì)象復(fù)制到新空間中。于是,關(guān)系較近的對(duì)象被放在距離較近的內(nèi)存空間中的可能性會(huì)提高,這被稱為局部性。局部性高的情況下,內(nèi)存緩存會(huì)更容易有效運(yùn)作,程序的運(yùn)行性能也能夠得到提高。
3、引用計(jì)數(shù)法引用計(jì)數(shù)(Reference Count)方式是GC算法中最簡(jiǎn)單也最容易實(shí)現(xiàn)的一種,它和標(biāo)記清除方式差不多是在同一時(shí)間發(fā)明出來(lái)的。它的基本原理是,在每個(gè)對(duì)象中保存該對(duì)象的引用計(jì)數(shù),當(dāng)引用發(fā)生增減時(shí)對(duì)計(jì)數(shù)進(jìn)行更新。引用計(jì)數(shù)的增減,一般發(fā)生在變量賦值、對(duì)象內(nèi)容更新、函數(shù)結(jié)束(局部變量不再被引用)等時(shí)間點(diǎn)。當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)變?yōu)?時(shí),則說(shuō)明它將來(lái)不會(huì)再被引用,因此可以釋放相應(yīng)的內(nèi)存空間。
圖3的(1)部分中,所有對(duì)象中都保存著自己被多少個(gè)其他對(duì)象進(jìn)行引用的數(shù)量(引用計(jì)數(shù)),圖中每個(gè)對(duì)象右上角的數(shù)字就是引用計(jì)數(shù)。圖中(2)部分中,當(dāng)對(duì)象引用發(fā)生變化時(shí),引用計(jì)數(shù)也跟著變化。在這里,由對(duì)象B到對(duì)象D的引用失效了,于是對(duì)象D的引用計(jì)數(shù)變?yōu)?。由于對(duì)象D的引用計(jì)數(shù)為0,因此由對(duì)象D到對(duì)象C和E的引用數(shù)也分別相應(yīng)減少。結(jié)果,對(duì)象E的引用計(jì)數(shù)也變?yōu)?,于是對(duì)象E也被釋放掉了。圖3的(3)部分中,引用計(jì)數(shù)變?yōu)?的對(duì)象被釋放,“存活”對(duì)象則保留了下來(lái)。大家應(yīng)該注意到,在整個(gè)GC處理過(guò)程中,并不需要對(duì)所有對(duì)象進(jìn)行掃描。
實(shí)現(xiàn)容易是引用計(jì)數(shù)算法最大的優(yōu)點(diǎn)。標(biāo)記清除和復(fù)制收集這些GC機(jī)制在實(shí)現(xiàn)上都有一定難度;而引用計(jì)數(shù)方式的話,凡是有些年頭的C++程序員(包括我在內(nèi)),應(yīng)該都曾經(jīng)實(shí)現(xiàn)過(guò)類似的機(jī)制,可以說(shuō)這種算法是相當(dāng)具有普遍性的。除此之外,當(dāng)對(duì)象不再被引用的瞬間就會(huì)被釋放,這也是一個(gè)優(yōu)點(diǎn)。其他的GC機(jī)制中,要預(yù)測(cè)一個(gè)對(duì)象何時(shí)會(huì)被釋放是很困難的,而在引用計(jì)數(shù)方式中則是立即被釋放的。而且,由于釋放操作是針對(duì)每個(gè)對(duì)象個(gè)別執(zhí)行的,因此和其他算法相比,由GC而產(chǎn)生的中斷時(shí)間(Pause time)就比較短,這也是一個(gè)優(yōu)點(diǎn)。
引用計(jì)數(shù)方式的缺點(diǎn)另一方面,這種方式也有缺點(diǎn)。引用計(jì)數(shù)最大的缺點(diǎn),就是無(wú)法釋放循環(huán)引用的對(duì)象。
圖4中,A、B、C三個(gè)對(duì)象沒(méi)有被其他對(duì)象引用,而是互相之間循環(huán)引用,因此它們的引用計(jì)數(shù)永遠(yuǎn)不會(huì)為0,結(jié)果這些對(duì)象就永遠(yuǎn)不會(huì)被釋放。引用計(jì)數(shù)的第二個(gè)缺點(diǎn),就是必須在引用發(fā)生增減時(shí)對(duì)引用計(jì)數(shù)做出正確的增減,而如果漏掉了某個(gè)增減的話,就會(huì)引發(fā)很難找到原因的內(nèi)存錯(cuò)誤。引用數(shù)忘了增加的話,會(huì)對(duì)不恰當(dāng)?shù)膶?duì)象進(jìn)行釋放;而引用數(shù)忘了減少的話,對(duì)象會(huì)一直殘留在內(nèi)存中,從而導(dǎo)致內(nèi)存泄漏。如果語(yǔ)言編譯器本身對(duì)引用計(jì)數(shù)進(jìn)行管理的話還好,否則,如果是手動(dòng)管理引用計(jì)數(shù)的話,那將成為孕育bug的溫床。
最后一個(gè)缺點(diǎn)就是,引用計(jì)數(shù)管理并不適合并行處理。如果多個(gè)線程同時(shí)對(duì)引用計(jì)數(shù)進(jìn)行增減的話,引用計(jì)數(shù)的值就可能會(huì)產(chǎn)生不一致的問(wèn)題(結(jié)果則會(huì)導(dǎo)致內(nèi)存錯(cuò)誤)。為了避免這種情況的發(fā)生,對(duì)引用計(jì)數(shù)的操作必須采用獨(dú)占的方式來(lái)進(jìn)行。如果引用操作頻繁發(fā)生,每次都要使用加鎖等并發(fā)控制機(jī)制的話,其開銷也是不可小覷的。綜上所述,引用計(jì)數(shù)方式的原理和實(shí)現(xiàn)雖然簡(jiǎn)單,但缺點(diǎn)也很多,因此最近基本上不再使用了。現(xiàn)在,依然采用引用計(jì)數(shù)方式的語(yǔ)言主要有Perl和Python,但它們?yōu)榱吮苊庋h(huán)引用的問(wèn)題,都配合使用了其他的GC機(jī)制。這些語(yǔ)言中,GC基本上是通過(guò)引用計(jì)數(shù)方式來(lái)進(jìn)行的,但偶爾也會(huì)用其他的算法來(lái)執(zhí)行GC,這樣就可以將引用計(jì)數(shù)方式無(wú)法回收的那些對(duì)象處理掉。
引用代碼的未來(lái)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65639.html
摘要:現(xiàn)在,通過(guò)對(duì)這三種方式進(jìn)行融合,出現(xiàn)了一些更加高級(jí)的方式。這樣一來(lái),需要掃描的對(duì)象數(shù)量就會(huì)大幅減少。像這樣以全部區(qū)域?yàn)閷?duì)象的操作被稱為完全回收或者大回收。在一般的算法中,作出這樣的保證是不可能的,因?yàn)楫a(chǎn)生的中斷時(shí)間與對(duì)象的數(shù)量和狀態(tài)有關(guān)。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的...
摘要:系統(tǒng)總運(yùn)行時(shí)間應(yīng)用程序耗時(shí)耗時(shí)。一般而言,頻率越低越好,通常增大堆空間可以有效降低垃圾回收發(fā)生的頻率,但是會(huì)增加回收時(shí)產(chǎn)生的停頓時(shí)間。反應(yīng)時(shí)間當(dāng)一個(gè)對(duì)象成為垃圾后,多長(zhǎng)時(shí)間內(nèi),它所占用的內(nèi)存空間會(huì)被釋放掉。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card table數(shù)據(jù)結(jié)構(gòu) J...
摘要:在一般應(yīng)用中,不會(huì)逃逸的局部對(duì)象所占的比例很大,如果能使用棧上分配,那大量的對(duì)象就會(huì)隨著方法的結(jié)束而自動(dòng)銷毀了,垃圾收集系統(tǒng)的壓力將會(huì)小很多。相關(guān)參數(shù)設(shè)置大對(duì)象直接進(jìn)入年老代的閾值,當(dāng)對(duì)象大小超過(guò)這個(gè)值時(shí),將直接在年老代分配。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card t...
摘要:系列垃圾回收基礎(chǔ)的編譯策略的三大基礎(chǔ)算法的三大高級(jí)算法策略的評(píng)價(jià)指標(biāo)信息查看通用日志解讀的數(shù)據(jù)結(jié)構(gòu)類初始化順序?qū)ο蠼Y(jié)構(gòu)及大小計(jì)算的類加載機(jī)制對(duì)象分配簡(jiǎn)要流程年老代過(guò)大有什么影響空間溢出實(shí)例關(guān)于線程與序本文主要講述如何查看應(yīng)用的信息。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的car...
摘要:系列垃圾回收基礎(chǔ)的編譯策略的三大基礎(chǔ)算法的三大高級(jí)算法策略的評(píng)價(jià)指標(biāo)信息查看通用日志解讀的數(shù)據(jù)結(jié)構(gòu)類初始化順序?qū)ο蠼Y(jié)構(gòu)及大小計(jì)算的類加載機(jī)制對(duì)象分配簡(jiǎn)要流程年老代過(guò)大有什么影響空間溢出實(shí)例關(guān)于線程與序本文主要講述日志的解讀。 jvm系列 垃圾回收基礎(chǔ) JVM的編譯策略 GC的三大基礎(chǔ)算法 GC的三大高級(jí)算法 GC策略的評(píng)價(jià)指標(biāo) JVM信息查看 GC通用日志解讀 jvm的card ta...
閱讀 743·2021-10-09 09:44
閱讀 2016·2021-09-22 15:54
閱讀 5057·2021-09-22 10:55
閱讀 1442·2019-08-29 18:41
閱讀 777·2019-08-29 11:24
閱讀 2104·2019-08-28 18:20
閱讀 1030·2019-08-26 11:51
閱讀 3049·2019-08-26 11:00