日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Java垃圾回收機(jī)制

 奮斗的小烏龜52 2015-11-20

綜合了若干人的blog~

1. 垃圾回收的意義
  在C++中,對(duì)象所占的內(nèi)存在程序結(jié)束運(yùn)行之前一直被占用,在明確釋放之前不能分配給其它對(duì)象;而在Java中,當(dāng)沒(méi)有對(duì)象引用指向原先分配給某個(gè)對(duì)象的內(nèi)存時(shí),該內(nèi)存便成為垃圾。JVM的一個(gè)系統(tǒng)級(jí)線程會(huì)自動(dòng)釋放該內(nèi)存塊。垃圾回收意味著程序不再需要的對(duì)象是"無(wú)用信息",這些信息將被丟棄。當(dāng)一個(gè)對(duì)象不再被引用的時(shí)候,內(nèi)存回收它占領(lǐng)的空間,以便空間被后來(lái)的新對(duì)象使用。事實(shí)上,除了釋放沒(méi)用的對(duì)象,垃圾回收也可以清除內(nèi)存記錄碎片。由于創(chuàng)建對(duì)象和垃圾回收器釋放丟棄對(duì)象所占的內(nèi)存空間,內(nèi)存會(huì)出現(xiàn)碎片。碎片是分配給對(duì)象的內(nèi)存塊之間的空閑內(nèi)存洞。碎片整理將所占用的堆內(nèi)存移到堆的一端,JVM將整理出的內(nèi)存分配給新的對(duì)象。
  垃圾回收能自動(dòng)釋放內(nèi)存空間,減輕編程的負(fù)擔(dān)。這使Java 虛擬機(jī)具有一些優(yōu)點(diǎn)。首先,它能使編程效率提高。在沒(méi)有垃圾回收機(jī)制的時(shí)候,可能要花許多時(shí)間來(lái)解決一個(gè)難懂的存儲(chǔ)器問(wèn)題。在用Java語(yǔ)言編程的時(shí)候,靠垃圾回收機(jī)制可大大縮短時(shí)間。其次是它保護(hù)程序的完整性, 垃圾回收是Java語(yǔ)言安全性策略的一個(gè)重要部份。
  垃圾回收的一個(gè)潛在的缺點(diǎn)是它的開(kāi)銷影響程序性能。Java虛擬機(jī)必須追蹤運(yùn)行程序中有用的對(duì)象,而且最終釋放沒(méi)用的對(duì)象。這一個(gè)過(guò)程需要花費(fèi)處理器的時(shí)間。其次垃圾回收算法的不完備性,早先采用的某些垃圾回收算法就不能保證100%收集到所有的廢棄內(nèi)存。當(dāng)然隨著垃圾回收算法的不斷改進(jìn)以及軟硬件運(yùn)行效率的不斷提升,這些問(wèn)題都可以迎刃而解。
2. 垃圾收集的算法分析
  Java語(yǔ)言規(guī)范沒(méi)有明確地說(shuō)明JVM使用哪種垃圾回收算法,但是任何一種垃圾回收算法一般要做2件基本的事情:(1)發(fā)現(xiàn)無(wú)用信息對(duì)象;(2)回收被無(wú)用對(duì)象占用的內(nèi)存空間,使該空間可被程序再次使用。
  大多數(shù)垃圾回收算法使用了根集(root set)這個(gè)概念;所謂根集就是正在執(zhí)行的Java程序可以訪問(wèn)的引用變量的集合(包括局部變量、參數(shù)、類變量),程序可以使用引用變量訪問(wèn)對(duì)象的屬性和調(diào)用對(duì)象的方法。垃圾回收首先需要確定從根開(kāi)始哪些是可達(dá)的和哪些是不可達(dá)的,從根集可達(dá)的對(duì)象都是活動(dòng)對(duì)象,它們不能作為垃圾被回收,這也包括從根集間接可達(dá)的對(duì)象。而根集通過(guò)任意路徑不可達(dá)的對(duì)象符合垃圾收集的條件,應(yīng)該被回收。下面介紹幾個(gè)常用的算法。
  2.1. 引用計(jì)數(shù)法(Reference Counting Collector)
  引用計(jì)數(shù)法是唯一沒(méi)有使用根集的垃圾回收的法,該算法使用引用計(jì)數(shù)器來(lái)區(qū)分存活對(duì)象和不再使用的對(duì)象。一般來(lái)說(shuō),堆中的每個(gè)對(duì)象對(duì)應(yīng)一個(gè)引用計(jì)數(shù)器。當(dāng)每一次創(chuàng)建一個(gè)對(duì)象并賦給一個(gè)變量時(shí),引用計(jì)數(shù)器置為1。當(dāng)對(duì)象被賦給任意變量時(shí),引用計(jì)數(shù)器每次加1當(dāng)對(duì)象出了作用域后(該對(duì)象丟棄不再使用),引用計(jì)數(shù)器減1,一旦引用計(jì)數(shù)器為0,對(duì)象就滿足了垃圾收集的條件。
  基于引用計(jì)數(shù)器的垃圾收集器運(yùn)行較快,不會(huì)長(zhǎng)時(shí)間中斷程序執(zhí)行,適宜地必須實(shí)時(shí)運(yùn)行的程序。但引用計(jì)數(shù)器增加了程序執(zhí)行的開(kāi)銷,因?yàn)槊看螌?duì)象賦給新的變量,計(jì)數(shù)器加1,而每次現(xiàn)有對(duì)象出了作用域生,計(jì)數(shù)器減1。
  2.2. tracing算法(Tracing Collector)
  tracing算法是為了解決引用計(jì)數(shù)法的問(wèn)題而提出,它使用了根集的概念?;趖racing算法的垃圾收集器從根集開(kāi)始掃描,識(shí)別出哪些對(duì)象可達(dá),哪些對(duì)象不可達(dá),并用某種方式標(biāo)記可達(dá)對(duì)象,例如對(duì)每個(gè)可達(dá)對(duì)象設(shè)置一個(gè)或多個(gè)位。在掃描識(shí)別過(guò)程中,基于tracing算法的垃圾收集也稱為標(biāo)記和清除(mark-and-sweep)垃圾收集器.
  2.3. compacting算法(Compacting Collector)
  為了解決堆碎片問(wèn)題,基于tracing的垃圾回收吸收了Compacting算法的思想,在清除的過(guò)程中,算法將所有的對(duì)象移到堆的一端,堆的另一端就變成了一個(gè)相鄰的空閑內(nèi)存區(qū),收集器會(huì)對(duì)它移動(dòng)的所有對(duì)象的所有引用進(jìn)行更新,使得這些引用在新的位置能識(shí)別原來(lái)的對(duì)象。在基于Compacting算法的收集器的實(shí)現(xiàn)中,一般增加句柄和句柄表。
  2.4. copying算法(Coping Collector)
  該算法的提出是為了克服句柄的開(kāi)銷和解決堆碎片的垃圾回收。它開(kāi)始時(shí)把堆分成一個(gè)對(duì)象區(qū)和多個(gè)空閑區(qū),程序從對(duì)象區(qū)為對(duì)象分配空間,當(dāng)對(duì)象滿了,基于coping算法的垃圾回收就從根集中掃描活動(dòng)對(duì)象,并將每個(gè)活動(dòng)對(duì)象復(fù)制到空閑區(qū)(使得活動(dòng)對(duì)象所占的內(nèi)存之間沒(méi)有空閑間隔),這樣空閑區(qū)變成了對(duì)象區(qū),原來(lái)的對(duì)象區(qū)變成了空閑區(qū),程序會(huì)在新的對(duì)象區(qū)中分配內(nèi)存。
  一種典型的基于coping算法的垃圾回收是stop-and-copy算法,它將堆分成對(duì)象區(qū)和空閑區(qū)域區(qū),在對(duì)象區(qū)與空閑區(qū)域的切換過(guò)程中,程序暫停執(zhí)行。
  2.5. generation算法(Generational Collector)
  stop-and-copy垃圾收集器的一個(gè)缺陷是收集器必須復(fù)制所有的活動(dòng)對(duì)象,這增加了程序等待時(shí)間,這是coping算法低效的原因。在程序設(shè)計(jì)中有這樣的規(guī)律:多數(shù)對(duì)象存在的時(shí)間比較短,少數(shù)的存在時(shí)間比較長(zhǎng)。因此,generation算法將堆分成兩個(gè)或多個(gè),每個(gè)子堆作為對(duì)象的一代 (generation)。由于多數(shù)對(duì)象存在的時(shí)間比較短,隨著程序丟棄不使用的對(duì)象,垃圾收集器將從最年輕的子堆中收集這些對(duì)象。在分代式的垃圾收集器運(yùn)行后,上次運(yùn)行存活下來(lái)的對(duì)象移到下一最高代的子堆中,由于老一代的子堆不會(huì)經(jīng)常被回收,因而節(jié)省了時(shí)間。
  2.6. adaptive算法(Adaptive Collector)
  在特定的情況下,一些垃圾收集算法會(huì)優(yōu)于其它算法。基于Adaptive算法的垃圾收集器就是監(jiān)控當(dāng)前堆的使用情況,并將選擇適當(dāng)算法的垃圾收集器。

3. System.gc()方法

      命令行參數(shù)透視垃圾收集器的運(yùn)行
  使用System.gc()可以不管JVM使用的是哪一種垃圾回收的算法,都可以請(qǐng)求Java的垃圾回收。在命令行中有一個(gè)參數(shù)-verbosegc可以查看Java使用的堆內(nèi)存的情況,它的格式如下:
  java -verbosegc classfile
  可以看個(gè)例子:
  

  1. class TestGC  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.       new TestGC();  
  6.       System.gc();  
  7.       System.runFinalization();  
  8.    }  
  9. }  

     
  在這個(gè)例子中,一個(gè)新的對(duì)象被創(chuàng)建,由于它沒(méi)有使用,所以該對(duì)象迅速地變?yōu)椴豢蛇_(dá),程序編譯后,執(zhí)行命令: java -verbosegc TestGC 后結(jié)果為:
  [Full GC 168K->97K(1984K), 0.0253873 secs]
  機(jī)器的環(huán)境為,Windows 2000 + JDK1.3.1,箭頭前后的數(shù)據(jù)168K和97K分別表示垃圾收集GC前后所有存活對(duì)象使用的內(nèi)存容量,說(shuō)明有168K-97K=71K的對(duì)象容量被回收,括號(hào)內(nèi)的數(shù)據(jù)1984K為堆內(nèi)存的總?cè)萘浚占枰臅r(shí)間是0.0253873秒(這個(gè)時(shí)間在每次執(zhí)行的時(shí)候會(huì)有所不同)。

      需要注意的是,調(diào)用System.gc()也僅僅是一個(gè)請(qǐng)求(建議)。JVM接受這個(gè)消息后,并不是立即做垃圾回收,而只是對(duì)幾個(gè)垃圾回收算法做了加權(quán),使垃圾回收操作容易發(fā)生,或提早發(fā)生,或回收較多而已。

4. finalize()方法

      在JVM垃圾回收器收集一個(gè)對(duì)象之前,一般要求程序調(diào)用適當(dāng)?shù)姆椒ㄡ尫刨Y源,但在沒(méi)有明確釋放資源的情況下,Java提供了缺省機(jī)制來(lái)終止該對(duì)象心釋放資源,這個(gè)方法就是finalize()。它的原型為:
  protected void finalize() throws Throwable
  在finalize()方法返回之后,對(duì)象消失,垃圾收集開(kāi)始執(zhí)行。原型中的throws Throwable表示它可以拋出任何類型的異常。
  之所以要使用finalize(),是存在著垃圾回收器不能處理的特殊情況。假定你的對(duì)象(并非使用new方法)獲得了一塊“特殊”的內(nèi)存區(qū)域,由于垃圾回收器只知道那些顯示地經(jīng)由new分配的內(nèi)存空間,所以它不知道該如何釋放這塊“特殊”的內(nèi)存區(qū)域,那么這個(gè)時(shí)候java允許在類中定義一個(gè)由finalize()方法。

      特殊的區(qū)域例如:1)由于在分配內(nèi)存的時(shí)候可能采用了類似 C語(yǔ)言的做法,而非JAVA的通常new做法。這種情況主要發(fā)生在native method中,比如native method調(diào)用了C/C++方法malloc()函數(shù)系列來(lái)分配存儲(chǔ)空間,但是除非調(diào)用free()函數(shù),否則這些內(nèi)存空間將不會(huì)得到釋放,那么這個(gè)時(shí)候就可能造成內(nèi)存泄漏。但是由于free()方法是在C/C++中的函數(shù),所以finalize()中可以用本地方法來(lái)調(diào)用它。以釋放這些“特殊”的內(nèi)存空間。2)又或者打開(kāi)的文件資源,這些資源不屬于垃圾回收器的回收范圍。
      換言之,finalize()的主要用途是釋放一些其他做法開(kāi)辟的內(nèi)存空間,以及做一些清理工作。因?yàn)樵贘AVA中并沒(méi)有提夠像“析構(gòu)”函數(shù)或者類似概念的函數(shù),要做一些類似清理工作的時(shí)候,必須自己動(dòng)手創(chuàng)建一個(gè)執(zhí)行清理工作的普通方法,也就是override Object這個(gè)類中的finalize()方法。例如,假設(shè)某一個(gè)對(duì)象在創(chuàng)建過(guò)程中會(huì)將自己繪制到屏幕上,如果不是明確地從屏幕上將其擦出,它可能永遠(yuǎn)都不會(huì)被清理。如果在finalize()加入某一種擦除功能,當(dāng)GC工作時(shí),finalize()得到了調(diào)用,圖像就會(huì)被擦除。要是GC沒(méi)有發(fā)生,那么這個(gè)圖像就會(huì)

被一直保存下來(lái)。

      一旦垃圾回收器準(zhǔn)備好釋放對(duì)象占用的存儲(chǔ)空間,首先會(huì)去調(diào)用finalize()方法進(jìn)行一些必要的清理工作。只有到下一次再進(jìn)行垃圾回收動(dòng)作的時(shí)候,才會(huì)真正釋放這個(gè)對(duì)象所占用的內(nèi)存空間。
  在普通的清除工作中,為清除一個(gè)對(duì)象,那個(gè)對(duì)象的用戶必須在希望進(jìn)行清除的地點(diǎn)調(diào)用一個(gè)清除方法。這與C++"析構(gòu)函數(shù)"的概念稍有抵觸。在C++中,所有對(duì)象都會(huì)破壞(清除)?;蛘邠Q句話說(shuō),所有對(duì)象都"應(yīng)該"破壞。若將C++對(duì)象創(chuàng)建成一個(gè)本地對(duì)象,比如在堆棧中創(chuàng)建(在Java中是不可能的,Java都在堆中),那么清除或破壞工作就會(huì)在"結(jié)束花括號(hào)"所代表的、創(chuàng)建這個(gè)對(duì)象的作用域的末尾進(jìn)行。若對(duì)象是用new創(chuàng)建的(類似于Java),那么當(dāng)程序員調(diào)用C++的 delete命令時(shí)(Java沒(méi)有這個(gè)命令),就會(huì)調(diào)用相應(yīng)的析構(gòu)函數(shù)。若程序員忘記了,那么永遠(yuǎn)不會(huì)調(diào)用析構(gòu)函數(shù),我們最終得到的將是一個(gè)內(nèi)存"漏洞",另外還包括對(duì)象的其他部分永遠(yuǎn)不會(huì)得到清除。
  相反,Java不允許我們創(chuàng)建本地(局部)對(duì)象--無(wú)論如何都要使用new。但在Java中,沒(méi)有"delete"命令來(lái)釋放對(duì)象,因?yàn)槔厥掌鲿?huì)幫助我們自動(dòng)釋放存儲(chǔ)空間。所以如果站在比較簡(jiǎn)化的立場(chǎng),我們可以說(shuō)正是由于存在垃圾回收機(jī)制,所以Java沒(méi)有析構(gòu)函數(shù)。然而,隨著以后學(xué)習(xí)的深入,就會(huì)知道垃圾收集器的存在并不能完全消除對(duì)析構(gòu)函數(shù)的需要,或者說(shuō)不能消除對(duì)析構(gòu)函數(shù)代表的那種機(jī)制的需要(原因見(jiàn)下一段。另外finalize()函數(shù)是在垃圾回收器準(zhǔn)備釋放對(duì)象占用的存儲(chǔ)空間的時(shí)候被調(diào)用的,絕對(duì)不能直接調(diào)用finalize(),所以應(yīng)盡量避免用它)。若希望執(zhí)行除釋放存儲(chǔ)空間之外的其他某種形式的清除工作,仍然必須調(diào)用Java中的一個(gè)方法。它等價(jià)于C++的析構(gòu)函數(shù),只是沒(méi)后者方便。
      在C++中所有的對(duì)象運(yùn)用delete()一定會(huì)被銷毀,而JAVA里的對(duì)象并非總會(huì)被垃圾回收器回收。In another word, 1 對(duì)象可能不被垃圾回收,2 垃圾回收并不等于“析構(gòu)”,3 垃圾回收只與內(nèi)存有關(guān)。也就是說(shuō),并不是如果一個(gè)對(duì)象不再被使用,是不是要在finalize()中釋放這個(gè)對(duì)象中含有的其它對(duì)象呢?不是的。因?yàn)闊o(wú)論對(duì)象是如何創(chuàng)建的,垃圾回收器都會(huì)負(fù)責(zé)釋放那些對(duì)象占有的內(nèi)存。

5. 觸發(fā)主GC(Garbage Collector)的條件

  JVM進(jìn)行次GC的頻率很高,但因?yàn)檫@種GC占用時(shí)間極短,所以對(duì)系統(tǒng)產(chǎn)生的影響不大。更值得關(guān)注的是主GC的觸發(fā)條件,因?yàn)樗鼘?duì)系統(tǒng)影響很明顯??偟膩?lái)說(shuō),有兩個(gè)條件會(huì)觸發(fā)主GC:

  1)當(dāng)應(yīng)用程序空閑時(shí),即沒(méi)有應(yīng)用線程在運(yùn)行時(shí),GC會(huì)被調(diào)用。因?yàn)镚C在優(yōu)先級(jí)最低的線程中進(jìn)行,所以當(dāng)應(yīng)用忙時(shí),GC線程就不會(huì)被調(diào)用,但以下條件除外。

  2)Java堆內(nèi)存不足時(shí),GC會(huì)被調(diào)用。當(dāng)應(yīng)用線程在運(yùn)行,并在運(yùn)行過(guò)程中創(chuàng)建新對(duì)象,若這時(shí)內(nèi)存空間不足,JVM就會(huì)強(qiáng)制地調(diào)用GC線程,以便回收內(nèi)存用于新的分配。若GC一次之后仍不能滿足內(nèi)存分配的要求,JVM會(huì)再進(jìn)行兩次GC作進(jìn)一步的嘗試,若仍無(wú)法滿足要求,則 JVM將報(bào)“out of memory”的錯(cuò)誤,Java應(yīng)用將停止。

  由于是否進(jìn)行主GC由JVM根據(jù)系統(tǒng)環(huán)境決定,而系統(tǒng)環(huán)境在不斷的變化當(dāng)中,所以主GC的運(yùn)行具有不確定性,無(wú)法預(yù)計(jì)它何時(shí)必然出現(xiàn),但可以確定的是對(duì)一個(gè)長(zhǎng)期運(yùn)行的應(yīng)用來(lái)說(shuō),其主GC是反復(fù)進(jìn)行的。

6. 減少GC開(kāi)銷的措施

  根據(jù)上述GC的機(jī)制,程序的運(yùn)行會(huì)直接影響系統(tǒng)環(huán)境的變化,從而影響GC的觸發(fā)。若不針對(duì)GC的特點(diǎn)進(jìn)行設(shè)計(jì)和編碼,就會(huì)出現(xiàn)內(nèi)存駐留等一系列負(fù)面影響。為了避免這些影響,基本的原則就是盡可能地減少垃圾和減少GC過(guò)程中的開(kāi)銷。具體措施包括以下幾個(gè)方面:

  (1)不要顯式調(diào)用System.gc()

  此函數(shù)建議JVM進(jìn)行主GC,雖然只是建議而非一定,但很多情況下它會(huì)觸發(fā)主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數(shù)。

  (2)盡量減少臨時(shí)對(duì)象的使用

  臨時(shí)對(duì)象在跳出函數(shù)調(diào)用后,會(huì)成為垃圾,少用臨時(shí)變量就相當(dāng)于減少了垃圾的產(chǎn)生,從而延長(zhǎng)了出現(xiàn)上述第二個(gè)觸發(fā)條件出現(xiàn)的時(shí)間,減少了主GC的機(jī)會(huì)。

  (3)對(duì)象不用時(shí)最好顯式置為Null

  一般而言,為Null的對(duì)象都會(huì)被作為垃圾處理,所以將不用的對(duì)象顯式地設(shè)為Null,有利于GC收集器判定垃圾,從而提高了GC的效率。

  (4)盡量使用StringBuffer,而不用String來(lái)累加字符串

  由于String是固定長(zhǎng)的字符串對(duì)象,累加String對(duì)象時(shí),并非在一個(gè)String對(duì)象中擴(kuò)增,而是重新創(chuàng)建新的String對(duì)象,如Str5=Str1+Str2+Str3+Str4,這條語(yǔ)句執(zhí)行過(guò)程中會(huì)產(chǎn)生多個(gè)垃圾對(duì)象,因?yàn)閷?duì)次作“+”操作時(shí)都必須創(chuàng)建新的String對(duì)象,但這些過(guò)渡對(duì)象對(duì)系統(tǒng)來(lái)說(shuō)是沒(méi)有實(shí)際意義的,只會(huì)增加更多的垃圾。避免這種情況可以改用StringBuffer來(lái)累加字符串,因StringBuffer是可變長(zhǎng)的,它在原有基礎(chǔ)上進(jìn)行擴(kuò)增,不會(huì)產(chǎn)生中間對(duì)象。

  (5)能用基本類型如Int,Long,就不用Integer,Long對(duì)象

  基本類型變量占用的內(nèi)存資源比相應(yīng)對(duì)象占用的少得多,如果沒(méi)有必要,最好使用基本變量。

  (6)盡量少用靜態(tài)對(duì)象變量

  靜態(tài)變量屬于全局變量,不會(huì)被GC回收,它們會(huì)一直占用內(nèi)存。

  (7)分散對(duì)象創(chuàng)建或刪除的時(shí)間

  集中在短時(shí)間內(nèi)大量創(chuàng)建新對(duì)象,特別是大對(duì)象,會(huì)導(dǎo)致突然需要大量?jī)?nèi)存,JVM在面臨這種情況時(shí),只能進(jìn)行主GC,以回收內(nèi)存或整合內(nèi)存碎片,從而增加主GC的頻率。集中刪除對(duì)象,道理也是一樣的。它使得突然出現(xiàn)了大量的垃圾對(duì)象,空閑空間必然減少,從而大大增加了下一次創(chuàng)建新對(duì)象時(shí)強(qiáng)制主GC的機(jī)會(huì)。

 

      下面這個(gè)例子向大家展示了垃圾收集所經(jīng)歷的過(guò)程,并對(duì)前面的陳述進(jìn)行了總結(jié)。

     

  1. class Chair {  
  2.   static boolean gcrun = false;  
  3.   static boolean f = false;  
  4.   static int created = 0;  
  5.   static int finalized = 0;  
  6.   int i;  
  7.   Chair() {  
  8.       i = ++created;  
  9.       if(created == 47)  
  10.          System.out.println("Created 47");  
  11.   }  
  12.   protected void finalize() {  
  13.       if(!gcrun) {  
  14.          gcrun = true;  
  15.          System.out.println("Beginning to finalize after " + created + " Chairs have been created");  
  16.       }  
  17.       if(i == 47) {  
  18.          System.out.println("Finalizing Chair #47, " +"Setting flag to stop Chair creation");  
  19.          f = true;  
  20.       }  
  21.       finalized++;  
  22.       if(finalized >= created)  
  23.          System.out.println("All " + finalized + " finalized");  
  24.   }  
  25. }  
  26.   
  27. public class Garbage {  
  28.   public static void main(String[] args) {  
  29.   if(args.length == 0) {  
  30.      System.err.println("Usage: /n" + "java Garbage before/n or:/n" + "java Garbage after");  
  31.      return;  
  32.   }  
  33.   while(!Chair.f) {  
  34.      new Chair();  
  35.      new String("To take up space");  
  36.   }  
  37.   System.out.println("After all Chairs have been created:/n" + "total created = " + Chair.created +  
  38.   ", total finalized = " + Chair.finalized);  
  39.   if(args[0].equals("before")) {  
  40.     System.out.println("gc():");  
  41.     System.gc();  
  42.     System.out.println("runFinalization():");  
  43.     System.runFinalization();  
  44.   }  
  45.   System.out.println("bye!");  
  46.   if(args[0].equals("after"))  
  47.      System.runFinalizersOnExit(true);  
  48.   }  
  49. }  

      上面這個(gè)程序創(chuàng)建了許多Chair對(duì)象,而且在垃圾收集器開(kāi)始運(yùn)行后的某些時(shí)候,程序會(huì)停止創(chuàng)建Chair。由于垃圾收集器可能在任何時(shí)間運(yùn)行,所以我們不能準(zhǔn)確知道它在何時(shí)啟動(dòng)。因此,程序用一個(gè)名為gcrun的標(biāo)記來(lái)指出垃圾收集器是否已經(jīng)開(kāi)始運(yùn)行。利用第二個(gè)標(biāo)記f,Chair可告訴main()它應(yīng)停止對(duì)象的生成。這兩個(gè)標(biāo)記都是在finalize()內(nèi)部設(shè)置的,它調(diào)用于垃圾收集期間。另兩個(gè)static變量--created以及 finalized--分別用于跟蹤已創(chuàng)建的對(duì)象數(shù)量以及垃圾收集器已進(jìn)行完收尾工作的對(duì)象數(shù)量。最后,每個(gè)Chair都有它自己的(非 static)int i,所以能跟蹤了解它具體的編號(hào)是多少。編號(hào)為47的Chair進(jìn)行完收尾工作后,標(biāo)記會(huì)設(shè)為true,最終結(jié)束Chair對(duì)象的創(chuàng)建過(guò)程。

7. 關(guān)于垃圾回收的幾點(diǎn)補(bǔ)充
  經(jīng)過(guò)上述的說(shuō)明,可以發(fā)現(xiàn)垃圾回收有以下的幾個(gè)特點(diǎn)
  (1)垃圾收集發(fā)生的不可預(yù)知性:由于實(shí)現(xiàn)了不同的垃圾回收算法和采用了不同的收集機(jī)制,所以它有可能是定時(shí)發(fā)生,有可能是當(dāng)出現(xiàn)系統(tǒng)空閑CPU資源時(shí)發(fā)生,也有可能是和原始的垃圾收集一樣,等到內(nèi)存消耗出現(xiàn)極限時(shí)發(fā)生,這與垃圾收集器的選擇和具體的設(shè)置都有關(guān)系。
 ?。?)垃圾收集的精確性:主要包括2 個(gè)方面:(a)垃圾收集器能夠精確標(biāo)記活著的對(duì)象;(b)垃圾收集器能夠精確地定位對(duì)象之間的引用關(guān)系。前者是完全地回收所有廢棄對(duì)象的前提,否則就可能造成內(nèi)存泄漏。而后者則是實(shí)現(xiàn)歸并和復(fù)制等算法的必要條件。所有不可達(dá)對(duì)象都能夠可靠地得到回收,所有對(duì)象都能夠重新分配,允許對(duì)象的復(fù)制和對(duì)象內(nèi)存的縮并,這樣就有效地防止內(nèi)存的支離破碎。
 ?。?)現(xiàn)在有許多種不同的垃圾收集器,每種有其算法且其表現(xiàn)各異,既有當(dāng)垃圾收集開(kāi)始時(shí)就停止應(yīng)用程序的運(yùn)行,又有當(dāng)垃圾收集開(kāi)始時(shí)也允許應(yīng)用程序的線程運(yùn)行,還有在同一時(shí)間垃圾收集多線程運(yùn)行。
 ?。?)垃圾收集的實(shí)現(xiàn)和具體的JVM 以及JVM的內(nèi)存模型有非常緊密的關(guān)系。不同的JVM 可能采用不同的垃圾收集,而JVM 的內(nèi)存模型決定著該JVM可以采用哪些類型垃圾收集。現(xiàn)在,HotSpot 系列JVM中的內(nèi)存系統(tǒng)都采用先進(jìn)的面向?qū)ο蟮目蚣茉O(shè)計(jì),這使得該系列JVM都可以采用最先進(jìn)的垃圾收集。
 ?。?)隨著技術(shù)的發(fā)展,現(xiàn)代垃圾收集技術(shù)提供許多可選的垃圾收集器,而且在配置每種收集器的時(shí)候又可以設(shè)置不同的參數(shù),這就使得根據(jù)不同的應(yīng)用環(huán)境獲得最優(yōu)的應(yīng)用性能成為可能。
  針對(duì)以上特點(diǎn),我們?cè)谑褂玫臅r(shí)候要注意
  (1)不要試圖去假定垃圾收集發(fā)生的時(shí)間,這一切都是未知的。比如,方法中的一個(gè)臨時(shí)對(duì)象在方法調(diào)用完畢后就變成了無(wú)用對(duì)象,這個(gè)時(shí)候它的內(nèi)存就可以被釋放。
  (2)Java中提供了一些和垃圾收集打交道的類,而且提供了一種強(qiáng)行執(zhí)行垃圾收集的方法--調(diào)用System.gc(),但這同樣是個(gè)不確定的方法。Java 中并不保證每次調(diào)用該方法就一定能夠啟動(dòng)垃圾收集,它只不過(guò)會(huì)向JVM發(fā)出這樣一個(gè)申請(qǐng),到底是否真正執(zhí)行垃圾收集,一切都是個(gè)未知數(shù)。
  (3)挑選適合自己的垃圾收集器。一般來(lái)說(shuō),如果系統(tǒng)沒(méi)有特殊和苛刻的性能要求,可以采用JVM的缺省選項(xiàng)。否則可以考慮使用有針對(duì)性的垃圾收集器,比如增量收集器就比較適合實(shí)時(shí)性要求較高的系統(tǒng)之中。系統(tǒng)具有較高的配置,有比較多的閑置資源,可以考慮使用并行標(biāo)記/清除收集器。
  (4)關(guān)鍵的也是難把握的問(wèn)題是內(nèi)存泄漏。良好的編程習(xí)慣和嚴(yán)謹(jǐn)?shù)木幊虘B(tài)度永遠(yuǎn)是最重要的,不要讓自己的一個(gè)小錯(cuò)誤導(dǎo)致內(nèi)存出現(xiàn)大漏洞。
  (5)盡早釋放無(wú)用對(duì)象的引用。大多數(shù)程序員在使用臨時(shí)變量的時(shí)候,都是讓引用變量在退出活動(dòng)域(scope)后,自動(dòng)設(shè)置為null,暗示垃圾收集器來(lái)收集該對(duì)象,還必須注意該引用的對(duì)象是否被監(jiān)聽(tīng),如果有,則要去掉監(jiān)聽(tīng)器,然后再賦空值。

 

參考:

http://blog.sina.com.cn/s/blog_544e54220100dv0q.html

http://java./base/839279_1.html

http://blog.sina.com.cn/s/blog_5ef35aa00100ky9o.html

http://hi.baidu.com/ou_yang_/blog/item/129f1a33486e1dfe1b4cff55.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多