java垃圾回收學(xué)習(xí)總結(jié)

              當(dāng)一個(gè)開發(fā)者真正了解JVM在java內(nèi)存回收實(shí)現(xiàn)之后,其開發(fā)出來(lái)的程序?qū)⒖梢愿痈咝В梢愿浞值乩糜邢薜膬?nèi)存,更快地釋放那些無(wú)用java對(duì)象所占用的內(nèi)存,并避免java程序的內(nèi)存泄露。
          1.Java垃圾回收器概述
              程序員需要通過(guò)關(guān)鍵字new創(chuàng)建java對(duì)象,即可視為java對(duì)象申請(qǐng)內(nèi)存空間,JVM會(huì)再堆內(nèi)存中為每個(gè)對(duì)象分配空間,當(dāng)一個(gè)java對(duì)象失去引用時(shí)JVM的垃圾回收機(jī)制會(huì)自動(dòng)清除它們,并回收它們所占用的內(nèi)存空間。
              對(duì)于JVM垃圾回收機(jī)制來(lái)說(shuō),是否回收一個(gè)對(duì)象的標(biāo)準(zhǔn)在于:是否還有引用變量引用該對(duì)象?
              也就是說(shuō),當(dāng)java對(duì)象被創(chuàng)建出來(lái)后,GC就會(huì)實(shí)時(shí)的地監(jiān)控每一個(gè)對(duì)象的運(yùn)行狀態(tài),包括對(duì)象的申請(qǐng),引用,被引用,賦值等。當(dāng)其監(jiān)控到某個(gè)對(duì)象不再被引用變量所引用時(shí),立即GC就會(huì)回收它所占用的空間。
              基本上,可以把JVM內(nèi)存中對(duì)象引用理解成一種有向圖,把引用變量,對(duì)象都當(dāng)成為有向圖的頂點(diǎn),將引用關(guān)系當(dāng)成圖的有向邊,有向邊總是從引用端指向被引用的java對(duì)象。GC是采用有向圖的方式來(lái)管理內(nèi)存中的對(duì)象,因此可以方便地循環(huán)引用問(wèn)題。
              當(dāng)一個(gè)對(duì)象在堆內(nèi)存中運(yùn)行時(shí),根據(jù)他在對(duì)應(yīng)有向圖的狀態(tài),可以把它所處的狀態(tài)分為以下3種:
                  a.可達(dá)狀態(tài):當(dāng)一個(gè)對(duì)象被創(chuàng)建后,有一個(gè)以上的引用變量引用它。在有向圖中可以從起始頂點(diǎn)導(dǎo)航帶該對(duì)象,那它就處于可達(dá)狀態(tài),程序可以通過(guò)引用變量來(lái)調(diào)用
                  b.可恢復(fù)狀態(tài):如果程序中某個(gè)對(duì)象不再有任何引用變量引用它,它將先進(jìn)圖可恢復(fù)狀態(tài),此時(shí)GC準(zhǔn)備回收該對(duì)象所占用的內(nèi)存。在回收之前,系統(tǒng)會(huì)調(diào)用可恢復(fù)狀態(tài)的對(duì)象的finalize方法進(jìn)行資源清理,如果重新讓一個(gè)以上引用變量引用該對(duì)象,則這個(gè)對(duì)象會(huì)變?yōu)榭蛇_(dá)狀態(tài),否則進(jìn)入不可達(dá)狀態(tài)
                  c.不可達(dá)狀態(tài):當(dāng)對(duì)象的所有關(guān)聯(lián)都被切斷,且系統(tǒng)調(diào)用的finalize方法依然沒(méi)有使該對(duì)象變?yōu)榭蛇_(dá)狀態(tài),那這個(gè)對(duì)象將永久性的失去引用,最后變?yōu)椴豢蛇_(dá)狀態(tài)。只有一個(gè)對(duì)象處于不可達(dá)狀態(tài)時(shí),系統(tǒng)才會(huì)真正的回收該對(duì)象所占有的資源。
          1.Java中對(duì)象引用分類

               為了更好的管理對(duì)象的引用,Java2平臺(tái)里面引入了java.lang.ref包,這個(gè)包中的類可以讓我們引用對(duì)象,而使得這些對(duì)象不用停留在內(nèi)存中。不僅僅如此,這個(gè)包提供了3個(gè)類SoftReference, WeakReference和 PhantomReference,它們分別代表了系統(tǒng)對(duì)對(duì)象的3鐘引用方式,軟引用,弱引用和虛引用。歸納起來(lái),java語(yǔ)言對(duì)對(duì)象的引用有如下4鐘:強(qiáng)引用,軟引用,弱引用和虛引用。

            Java中的對(duì)象引用主要有以下幾種類型:

            1)強(qiáng)可及對(duì)象(strongly reachable):

            可以通過(guò)強(qiáng)引用訪問(wèn)的對(duì)象,一般來(lái)說(shuō),我們平時(shí)寫代碼的方式都是使用的強(qiáng)引用對(duì)象,比如下邊的代碼段:

            StringBuilder builder= new StringBuilder();

            上邊代碼部分引用obj這個(gè)引用將引用內(nèi)存堆中的一個(gè)對(duì)象,這種情況下,只要obj的引用存在,垃圾回收器就永遠(yuǎn)不會(huì)釋放該對(duì)象的存儲(chǔ)空間。這種對(duì)象我們又成為強(qiáng)引用(Strong references),這種強(qiáng)引用方式就是Java語(yǔ)言的原生的Java引用,我們幾乎每天編程的時(shí)候都用到。上邊代碼JVM存儲(chǔ)了一個(gè)StringBuilder類型的對(duì)象的強(qiáng)引用在變量builder呢。強(qiáng)引用和GC的交互是這樣的,如果一個(gè)對(duì)象通過(guò)強(qiáng)引用可達(dá)或者通過(guò)強(qiáng)引用鏈可達(dá)的話這種對(duì)象就成為強(qiáng)可及對(duì)象,這種情況下的對(duì)象垃圾回收器不予理睬。如果我們開發(fā)過(guò)程不需要垃圾回器回收該對(duì)象,就直接將該對(duì)象賦為前引用。

            2)軟可及對(duì)象(softly reachable):

            不通過(guò)強(qiáng)引用訪問(wèn)的對(duì)象,即不是強(qiáng)可及對(duì)象,但是可以通過(guò)軟引用訪問(wèn)的對(duì)象就成為軟可及對(duì)象,軟可及對(duì)象就需要使用類SoftReference(java.lang.ref.SoftReference)。此種類型的引用主要用于內(nèi)存比較敏感的高速緩存,而且此種引用還是具有較強(qiáng)的引用功能,當(dāng)內(nèi)存不夠的時(shí)候GC會(huì)回收這類內(nèi)存,因此如果內(nèi)存充足的時(shí)候,這種引用通常不會(huì)被回收的。不僅僅如此,這種引用對(duì)象在JVM里面保證在拋出OutOfMemory異常之前,設(shè)置成為null。通俗地講,這種類型的引用保證在JVM內(nèi)存不足的時(shí)候全部被清楚,但是有個(gè)關(guān)鍵在于:垃圾收集器在運(yùn)行時(shí)是否釋放軟可及對(duì)象是不確定的,而且使用垃圾回收算法并不能保證一次性尋找到所有的軟可及對(duì)象。當(dāng)垃圾回收器每次運(yùn)行的時(shí)候都可以隨意釋放不是強(qiáng)可及對(duì)象占用的內(nèi)存,如果垃圾回收器找到了軟可及對(duì)象過(guò)后,可能會(huì)進(jìn)行以下操作:

          【1】將SoftReference對(duì)象的referent域設(shè)置成為null,從而使該對(duì)象不再引用heap對(duì)象。

          【2】SoftReference引用過(guò)的內(nèi)存堆上的對(duì)象一律被生命為finalizable。

          【3】當(dāng)內(nèi)存堆上的對(duì)象finalize()方法被運(yùn)行而且該對(duì)象占用的內(nèi)存被釋放,SoftReference對(duì)象就會(huì)被添加到它的ReferenceQueue,前提條件是ReferenceQueue本身是存在的。

            既然Java里面存在這樣的對(duì)象,那么我們?cè)诰帉懘a的時(shí)候如何創(chuàng)建這樣的對(duì)象呢?創(chuàng)建步驟如下:

            先創(chuàng)建一個(gè)對(duì)象,并使用普通引用方式【強(qiáng)引用】,然后再創(chuàng)建一個(gè)SoftReference來(lái)引用該對(duì)象,最后將普通引用設(shè)置為null,通過(guò)這樣的方式,這個(gè)對(duì)象就僅僅保留了一個(gè)SoftReference引用,同時(shí)這種情況我們所創(chuàng)建的對(duì)象就是SoftReference對(duì)象。一般情況下,我們可以使用該引用來(lái)完成Cache功能,就是前邊說(shuō)的用于高速緩存,保證最大限度使用內(nèi)存而不會(huì)引起內(nèi)存泄漏的情況。下邊的代碼段:

            public static void main(String args[])

            {

              //創(chuàng)建一個(gè)強(qiáng)可及對(duì)象

              A a = new A();

              //創(chuàng)建這個(gè)對(duì)象的軟引用SoftReference

              SoftReference sr = new SoftReference(a);

              //將強(qiáng)引用設(shè)置為空,以遍垃圾回收器回收強(qiáng)引用

              a = null;

              //下次使用該對(duì)象的操作

              if( sr != null ){

                a = (A)sr.get();

              }else{

                //這種情況就是由于內(nèi)存過(guò)低,已經(jīng)將軟引用釋放了,因此需要重新裝載一次

                a = new A();

                sr = new SoftReference(a);

              }

            }

            軟引用技術(shù)使得Java系統(tǒng)可以更好地管理內(nèi)存,保持系統(tǒng)穩(wěn)定,防止內(nèi)存泄漏,避免系統(tǒng)崩潰,因此在處理一些內(nèi)存占用大而且生命周期長(zhǎng)使用不頻繁的對(duì)象可以使用該技術(shù)。

            3)弱可及對(duì)象(weakly reachable):

            不是強(qiáng)可及對(duì)象同樣也不是軟可及對(duì)象,僅僅通過(guò)弱引用WeakReference(java.lang.ref.WeakReference)訪問(wèn)的對(duì)象,這種對(duì)象的用途在于規(guī)范化映射(canonicalized mapping),對(duì)于生存周期相對(duì)比較長(zhǎng)而且重新創(chuàng)建的時(shí)候開銷少的對(duì)象,弱引用也比較有用,和軟引用對(duì)象不同的是,垃圾回收器如果碰到了弱可及對(duì)象,將釋放WeakReference對(duì)象的內(nèi)存,但是垃圾回收器需要運(yùn)行很多次才能夠找到弱可及對(duì)象。弱引用對(duì)象在使用的時(shí)候,可以配合ReferenceQueue類使用,如果弱引用被回收,JVM就會(huì)把這個(gè)弱引用加入到相關(guān)的引用隊(duì)列中去。最簡(jiǎn)單的弱引用方法如以下代碼:

            WeakReference weakWidget = new WeakReference(classA);

            在上邊代碼里面,當(dāng)我們使用weakWidget.get()來(lái)獲取classA的時(shí)候,由于弱引用本身是無(wú)法阻止垃圾回收的,所以我們也許會(huì)拿到一個(gè)null返回。【*:這里提供一個(gè)小技巧,如果我們希望取得某個(gè)對(duì)象的信息,但是又不影響該對(duì)象的垃圾回收過(guò)程,我們就可以使用WeakReference來(lái)記住該對(duì)象,一般我們?cè)陂_發(fā)調(diào)試器和優(yōu)化器的時(shí)候使用這個(gè)是很好的一個(gè)手段。】

            如果上邊的代碼部分,我們通過(guò)weakWidget.get()返回的是null就證明該對(duì)象已經(jīng)被垃圾回收器回收了,而這種情況下弱引用對(duì)象就失去了使用價(jià)值,GC就會(huì)定義為需要進(jìn)行清除工作。這種情況下弱引用無(wú)法引用任何對(duì)象,所以在JVM里面就成為了一個(gè)死引用,這就是為什么我們有時(shí)候需要通過(guò)ReferenceQueue類來(lái)配合使用的原因,使用了ReferenceQueue過(guò)后,就使得我們更加容易監(jiān)視該引用的對(duì)象,如果我們通過(guò)一ReferenceQueue類來(lái)構(gòu)造一個(gè)若引用,當(dāng)若引用的對(duì)象已經(jīng)被回收的時(shí)候,系統(tǒng)將自動(dòng)使用對(duì)象引用隊(duì)列來(lái)代替對(duì)象引用,而且我們可以通過(guò)ReferenceQueue類的運(yùn)行來(lái)決定是否真正要從垃圾回收器里面將該死引用(Dead Reference)清除。

            弱引用代碼段:

            //創(chuàng)建普通引用對(duì)象

            MyObject object = new MyObject();

            //創(chuàng)建一個(gè)引用隊(duì)列

            ReferenceQueue rq = new ReferenceQueue();

            //使用引用隊(duì)列創(chuàng)建MyObject的弱引用

            WeakReference wr = new WeakReference(object,rq);

            這里提供兩個(gè)實(shí)在的場(chǎng)景來(lái)描述弱引用的相關(guān)用法:

            (1)你想給對(duì)象附加一些信息,于是你用一個(gè) Hashtable 把對(duì)象和附加信息關(guān)聯(lián)起來(lái)。你不停的把對(duì)象和附加信息放入 Hashtable 中,但是當(dāng)對(duì)象用完的時(shí)候,你不得不把對(duì)象再?gòu)?Hashtable 中移除,否則它占用的內(nèi)存變不會(huì)釋放。萬(wàn)一你忘記了,那么沒(méi)有從 Hashtable 中移除的對(duì)象也可以算作是內(nèi)存泄漏。理想的狀況應(yīng)該是當(dāng)對(duì)象用完時(shí),Hashtable 中的對(duì)象會(huì)自動(dòng)被垃圾收集器回收,不然你就是在做垃圾回收的工作。

            (2)你想實(shí)現(xiàn)一個(gè)圖片緩存,因?yàn)榧虞d圖片的開銷比較大。你將圖片對(duì)象的引用放入這個(gè)緩存,以便以后能夠重新使用這個(gè)對(duì)象。但是你必須決定緩存中的哪些圖片不再需要了,從而將引用從緩存中移除。不管你使用什么管理緩存的算法,你實(shí)際上都在處理垃圾收集的工作,更簡(jiǎn)單的辦法(除非你有特殊的需求,這也應(yīng)該是最好的辦法)是讓垃圾收集器來(lái)處理,由它來(lái)決定回收哪個(gè)對(duì)象。

            當(dāng)Java回收器遇到了弱引用的時(shí)候有可能會(huì)執(zhí)行以下操作:

           

          【1】將WeakReference對(duì)象的referent域設(shè)置成為null,從而使該對(duì)象不再引用heap對(duì)象。

          【2】WeakReference引用過(guò)的內(nèi)存堆上的對(duì)象一律被生命為finalizable。

          【3】當(dāng)內(nèi)存堆上的對(duì)象finalize()方法被運(yùn)行而且該對(duì)象占用的內(nèi)存被釋放,WeakReference對(duì)象就會(huì)被添加到它的ReferenceQueue,前提條件是ReferenceQueue本身是存在的。

            4)清除:

            當(dāng)引用對(duì)象的referent域設(shè)置為null,并且引用類在內(nèi)存堆中引用的對(duì)象聲明為可結(jié)束的時(shí)候,該對(duì)象就可以清除,清除不做過(guò)多的講述

            5)虛可及對(duì)象(phantomly reachable):

            不是強(qiáng)可及對(duì)象,也不是軟可及對(duì)象,同樣不是弱可及對(duì)象,之所以把虛可及對(duì)象放到最后來(lái)講,主要也是因?yàn)樗奶厥庑裕袝r(shí)候我們又稱之為“幽靈對(duì)象”,已經(jīng)結(jié)束的,可以通過(guò)虛引用來(lái)訪問(wèn)該對(duì)象。我們使用類PhantomReference(java.lang.ref.PhantomReference)來(lái)訪問(wèn),這個(gè)類只能用于跟蹤被引用對(duì)象進(jìn)行的收集,同樣的,可以用于執(zhí)行per-mortern清除操作。PhantomReference必須ReferenceQueue類一起使用。需要使用ReferenceQueue是因?yàn)樗軌虺洚?dāng)通知機(jī)制,當(dāng)垃圾收集器確定了某個(gè)對(duì)象是虛可及對(duì)象的時(shí)候,PhantomReference對(duì)象就被放在了它的ReferenceQueue上,這就是一個(gè)通知,表明PhantomReference引用的對(duì)象已經(jīng)結(jié)束,可以收集了,一般情況下我們剛好在對(duì)象內(nèi)存在回收之前采取該行為。這種引用不同于弱引用和軟引用,這種方式通過(guò)get()獲取到的對(duì)象總是返回null,僅僅當(dāng)這些對(duì)象在ReferenceQueue隊(duì)列里面的時(shí)候,我們可以知道它所引用的哪些對(duì)對(duì)象是死引用(Dead Reference)。而這種引用和弱引用的區(qū)別在于:

            弱引用(WeakReference)是在對(duì)象不可達(dá)的時(shí)候盡快進(jìn)入ReferenceQueue隊(duì)列的,在finalization方法執(zhí)行和垃圾回收之前是確實(shí)會(huì)發(fā)生的,理論上這類對(duì)象是不正確的對(duì)象,但是WeakReference對(duì)象可以繼續(xù)保持Dead狀態(tài),

            虛引用(PhantomReference)是在對(duì)象確實(shí)已經(jīng)從物理內(nèi)存中移除過(guò)后才進(jìn)入的ReferenceQueue隊(duì)列,而且get()方法會(huì)一直返回null

            當(dāng)垃圾回收器遇到了虛引用的時(shí)候?qū)⒂锌赡軋?zhí)行以下操作:

          【1】PhantomReference引用過(guò)的heap對(duì)象聲明為finalizable;

          【2】虛引用在堆對(duì)象釋放之前就添加到了它的ReferenceQueue里面,這種情況使得我們可以在堆對(duì)象被回收之前采取操作(*:再次提醒,PhantomReference對(duì)象必須經(jīng)過(guò)關(guān)聯(lián)的ReferenceQueue來(lái)創(chuàng)建,就是說(shuō)必須和ReferenceQueue類配合操作)

            看似沒(méi)有用處的虛引用,有什么用途呢?

          1.首先,我們可以通過(guò)虛引用知道對(duì)象究竟什么時(shí)候真正從內(nèi)存里面移除的,而且這也是唯一的途徑。

          2.虛引用避過(guò)了finalize()方法,因?yàn)閷?duì)于此方法的執(zhí)行而言,虛引用真正引用到的對(duì)象是異常對(duì)象,若在該方法內(nèi)要使用對(duì)象只能重建。一般情況垃圾回收器會(huì)輪詢兩次,一次標(biāo)記為finalization,第二次進(jìn)行真實(shí)的回收,而往往標(biāo)記工作不能實(shí)時(shí)進(jìn)行,或者垃圾回收其會(huì)等待一個(gè)對(duì)象去標(biāo)記finalization。這種情況很有可能引起MemoryOut,而使用虛引用這種情況就會(huì)完全避免。因?yàn)樘撘迷谝脤?duì)象的過(guò)程不會(huì)去使得這個(gè)對(duì)象由Dead復(fù)活,而且這種對(duì)象是可以在回收周期進(jìn)行回收的。

            在JVM內(nèi)部,虛引用比起使用finalize()方法更加安全一點(diǎn)而且更加有效。而finaliaze()方法回收在虛擬機(jī)里面實(shí)現(xiàn)起來(lái)相對(duì)簡(jiǎn)單,而且也可以處理大部分工作,所以我們?nèi)匀皇褂眠@種方式來(lái)進(jìn)行對(duì)象回收的掃尾操作,但是有了虛引用過(guò)后我們可以選擇是否手動(dòng)操作該對(duì)象使得程序更加高效完美。

          2.Java里面對(duì)象的生命周期

            在JVM運(yùn)行空間里面,對(duì)象整個(gè)聲明周期大致分為以下幾個(gè)階段:

            創(chuàng)建階段(Creating)->應(yīng)用階段(Using)->不可視階段(Invisible)->不可達(dá)階段(Unreachable)->可收集階段(Collected)->終結(jié)階段(Finalized)->釋放階段(Free)

            【1】創(chuàng)建階段:

            創(chuàng)建過(guò)程需要經(jīng)過(guò)其中幾步:

            為對(duì)象分配內(nèi)存空間

            開始構(gòu)造對(duì)象

            遞歸調(diào)用超類的構(gòu)造方法

            進(jìn)行對(duì)象實(shí)例初始化和變量初始化

            執(zhí)行構(gòu)造方法體

            【2】應(yīng)用階段特征:

            系統(tǒng)至少維護(hù)著對(duì)象的一個(gè)強(qiáng)引用(Strong Reference)

            所有該對(duì)象的引用全部是強(qiáng)引用,除非我們顯示聲明了軟引用、弱引用或者虛引用

            【3】不可是視階段:

            不可視階段就是我們?cè)趨^(qū)域代碼中不可以再引用它,就是強(qiáng)引用已經(jīng)消失,一般情況我們把這個(gè)時(shí)候的對(duì)象設(shè)置為null,其主要目的是讓JVM發(fā)現(xiàn)它,并且可以及時(shí)回收該對(duì)象所占用資源

            【4】不可到達(dá)階段:

            不可達(dá)階段的對(duì)象,在虛擬機(jī)所管理的對(duì)象引用根集合中再也找不到直接或間接的強(qiáng)引用,這些對(duì)象通常是指所有線程棧中的臨時(shí)變量以及相關(guān)引用,這種對(duì)象都是要預(yù)備回收的對(duì)象,但是這時(shí)候不能被GC直接回收。

            【5】可收集階段、終結(jié)階段、釋放階段:

            對(duì)象生命周期最后一個(gè)階段,這種階段的對(duì)象可能處于三種狀態(tài):

            垃圾回收器發(fā)現(xiàn)對(duì)象已經(jīng)不可達(dá)

            finalize方法已經(jīng)被執(zhí)行

               對(duì)象已經(jīng)被重用

          3.總結(jié)
          文章部分內(nèi)容引用自此文,也有自己學(xué)習(xí)過(guò)程中的一些心得,希望大家多交流

           

          posted on 2011-07-18 14:18 朝陽(yáng)zzz 閱讀(798) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2011年7月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          文章分類

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 应城市| 沈丘县| 大埔区| 陇南市| 淅川县| 孟村| 乌海市| 梧州市| 石柱| 嘉兴市| 加查县| 左权县| 明溪县| 荣昌县| 静宁县| 乌什县| 建湖县| 大田县| 永嘉县| 科技| 建昌县| 阳信县| 台北市| 仪征市| 阿鲁科尔沁旗| 涟水县| 黄平县| 集贤县| 永吉县| 宝山区| 手游| 凤台县| 永登县| 思南县| 板桥市| 增城市| 手游| 五莲县| 喀什市| 西乌珠穆沁旗| 霍林郭勒市|