天天看點

搜集整理java中GC的了解

    Java的垃圾回收機制是Java虛拟機提供的能力,用于在空閑時間以不定時的方式動态回收無任何引用的對象占據的記憶體空間。

需要注意的是:垃圾回收回收的是無任何引用的對象占據的記憶體空間而不是對象本身;

System.gc() Runtime.getRuntime().gc() 上面的方法調用時用于顯式通知JVM可以進行一次垃圾回收,但真正垃圾回收機制具體在什麼時間點開始發生動作這同樣是不

可預料的

==========================================================================================================

GC是垃圾收集的意思(Gabage Collection),記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導緻程式或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域進而達到自動回收記憶體的目的,Java語言沒有提供釋放已配置設定記憶體的顯示操作方法。

(2) 對于GC來說,當程式員建立對象時,GC就開始監控這個對象的位址、大小以及使用情況。通常,GC采用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式确定哪些對象是"可達的",哪些對象是"不可達的"。當GC确定一些對象為"不可達"時,GC就有責任回收這些記憶體空間。可以。程式員可以手動執行System.gc(),通知GC運作,但是Java語言規範并不保證GC一定會執行。

(3) 垃圾回收是一種動态存儲管理技術,它自動地釋放不再被程式引用的對象,當一個對象不再被引用的時候,按照特定的垃圾收集算法來實作資源自動回收的功能。

(4) System.gc();就是呼叫java虛拟機的垃圾回收器運作回收記憶體的垃圾。

(5) 當不存在對一個對象的引用時,我們就假定不再需要那個對象,那個對象所占有的存儲單元可以被收回,可通過System.gc()方法回收,但一般要把不再引用的對象标志為null為佳。

(6) 每個 Java 應用程式都有一個 Runtime 類執行個體,使應用程式能夠與其運作的環境相連接配接。可以通過 getRuntime 方法擷取目前運作時。 Runtime.getRuntime().gc();

(7) java.lang.System.gc()隻是java.lang.Runtime.getRuntime().gc()的簡寫,兩者的行為沒有任何不同。

(8) 唯一的差別就是System.gc()寫起來比Runtime.getRuntime().gc()簡單點. 其實基本沒什麼機會用得到這個指令, 因為這個指令隻是建議JVM安排GC運作, 還有可能完全被拒絕。 GC本身是會周期性的自動運作的,由JVM決定運作的時機,而且現在的版本有多種更智能的模式可以選擇,還會根據運作的機器自動去做選擇,就算真的有性能上的需求,也應該去對GC的運作機制進行微調,而不是通過使用這個指令來實作性能的優化。

===================================================================================================

GC的基本原理

Java的記憶體管理實際上就是對象的管理,其中包括對象的配置設定和釋放。

對于程式員來說,配置設定對象使用new關鍵字;釋放對象時,隻要将對象所有引用指派為null,讓程式不能夠再通路到這個對象,我們稱該對象為\"不可達的\".GC将負責回收所有\"不可達\"對象的記憶體空間。

對于GC來說,當程式員建立對象時,GC就開始監控這個對象的位址、大小以及使用情況。通常,GC采用有向圖的方式記錄和管理堆(heap)中的所有對象(詳見 參考資料1 )。通過這種方式确定哪些對象是\"可達的\",哪些對象是\"不可達的\".當GC确定一些對象為\"不可達\"時,GC就有責任回收這些記憶體空間。但是,為了保證GC能夠在不同平台實作的問題,Java規範對GC的很多行為都沒有進行嚴格的規定。例如,對于采用什麼類型的回收算法、什麼時候進行回收等重要問題都沒有明确的規定。是以,不同的JVM的實作者往往有不同的實作算法。這也給Java程式員的開發帶來行多不确定性。本文研究了幾個與GC工作相關的問題,努力減少這種不确定性給Java程式帶來的負面影響。

增量式GC( Incremental GC )

GC在JVM中通常是由一個或一組程序來實作的,它本身也和使用者程式一樣占用heap空間,運作時也占用CPU.當GC程序運作時,應用程式停止運作。是以,當GC運作時間較長時,使用者能夠感到 Java程式的停頓,另外一方面,如果GC運作時間太短,則可能對象回收率太低,這意味着還有很多應該回收的對象沒有被回收,仍然占用大量記憶體。是以,在設計GC的時候,就必須在停頓時間和回收率之間進行權衡。一個好的GC實作允許使用者定義自己所需要的設定,例如有些記憶體有限有裝置,對記憶體的使用量非常敏感,希望GC能夠準确的回收記憶體,它并不在意程式速度的放慢。另外一些實時網絡遊戲,就不能夠允許程式有長時間的中斷。增量式GC就是通過一定的回收算法,把一個長時間的中斷,劃分為很多個小的中斷,通過這種方式減少GC對使用者程式的影響。雖然,增量式GC在整體性能上可能不如普通GC的效率高,但是它能夠減少程式的最長停頓時間。

Sun JDK提供的HotSpot JVM就能支援增量式GC.HotSpot JVM預設GC方式為不使用增量GC,為了啟動增量GC,我們必須在運作Java程式時增加-Xincgc的參數。HotSpot JVM增量式GC的實作是采用Train GC算法。它的基本想法就是,将堆中的所有對象按照建立和使用情況進行分組(分層),将使用頻繁高和具有相關性的對象放在一隊中,随着程式的運作,不斷對組進行調整。當GC運作時,它總是先回收最老的(最近很少通路的)的對象,如果整組都為可回收對象,GC将整組回收。這樣,每次GC運作隻回收一定比例的不可達對象,保證程式的順暢運作。

詳解finalize函數

finalize是位于Object類的一個方法,該方法的通路修飾符為protected,由于所有類為Object的子類,是以使用者類很容易通路到這個方法。由于,finalize函數沒有自動實作鍊式調用,我們必須手動的實作,是以finalize函數的最後一個語句通常是super.finalize()。通過這種方式,我們可以實作從下到上實作finalize的調用,即先釋放自己的資源,然後再釋放父類的資源。

根據Java語言規範,JVM保證調用finalize函數之前,這個對象是不可達的,但是JVM不保證這個函數一定會被調用。另外,規範還保證finalize函數最多運作一次。

很多Java初學者會認為這個方法類似與C++中的析構函數,将很多對象、資源的釋放都放在這一函數裡面。其實,這不是一種很好的方式。原因有三,其一,GC為了能夠支援finalize函數,要對覆寫這個函數的對象作很多附加的工作。其二,在finalize運作完成之後,該對象可能變成可達的,GC還要再檢查一次該對象是否是可達的。是以,使用 finalize會降低GC的運作性能。其三,由于GC調用finalize的時間是不确定的,是以通過這種方式釋放資源也是不确定的。

通常,finalize用于一些不容易控制、并且非常重要資源的釋放,例如一些I/O的操作,資料的連接配接。這些資源的釋放對整個應用程式是非常關鍵的。在這種情況下,程式員應該以通過程式本身管理(包括釋放)這些資源為主,以finalize函數釋放資源方式為輔,形成一種雙保險的管理機制,而不應該僅僅依靠finalize來釋放資源。

下面給出一個例子說明,finalize函數被調用以後,仍然可能是可達的,同時也可說明一個對象的finalize隻可能運作一次。

-----------------------------------------------------

複制代碼

class MyObject{

Test main; //記錄Test對象,在finalize中時用于恢複可達性

public MyObject(Test t)

{

main=t; //儲存Test 對象

}

protected void finalize()

main.ref=this;// 恢複本對象,讓本對象可達

System.out.println(\"This is finalize\");//用于測試finalize隻運作一次

class Test {

MyObject ref;

public static void main(String[] args) {

Test test=new Test();

test.ref=new MyObject(test);

test.ref=null; //MyObject對象為不可達對象,finalize将被調用

System.gc();

if (test.ref!=null) System.out.println(\"My Object還活着\");

運作結果:

This is finalize

MyObject還活着

------------------------------------------------------------------------------------

此例子中,需要注意的是雖然MyObject對象在finalize中變成可達對象,但是下次回收時候,finalize卻不再被調用,因為finalize函數最多隻調用一次。

程式如何與GC進行互動

Java2增強了記憶體管理功能,增加了一個java.lang.ref包,其中定義了三種引用類。這三種引用類分别為SoftReference、WeakReference和 PhantomReference.通過使用這些引用類,程式員可以在一定程度與GC進行互動,以便改善GC的工作效率。這些引用類的引用強度介于可達對象和不可達對象之間。

建立一個引用對象也非常容易,例如如果你需要建立一個Soft Reference對象,那麼首先建立一個對象,并采用普通引用方式(可達對象);然後再建立一個SoftReference引用該對象;最後将普通引用設定為null.通過這種方式,這個對象就隻有一個Soft Reference引用。同時,我們稱這個對象為Soft Reference 對象。

Soft Reference的主要特點是據有較強的引用功能。隻有當記憶體不夠的時候,才進行回收這類記憶體,是以在記憶體足夠的時候,它們通常不被回收。另外,這些引用對象還能保證在Java抛出OutOfMemory 異常之前,被設定為null.它可以用于實作一些常用圖檔的緩存,實作Cache的功能,保證最大限度的使用記憶體而不引起OutOfMemory.以下給出這種引用類型的使用僞代碼;

------------------------------------------------------------------------

//申請一個圖像對象

Image p_w_picpath=new Image();//建立Image對象

//使用 p_w_picpath

//使用完了p_w_picpath,将它設定為soft 引用類型,并且釋放強引用;

SoftReference sr=new SoftReference(p_w_picpath);

p_w_picpath=null;

//下次使用時

if (sr!=null) p_w_picpath=sr.get();

else{

//由于GC由于低記憶體,已釋放p_w_picpath,是以需要重新裝載;

p_w_picpath=new Image();

sr=new SoftReference(p_w_picpath);

---------------------------------------------------------------------------------------

Weak引用對象與Soft引用對象的最大不同就在于:GC在進行回收時,需要通過算法檢查是否回收Soft引用對象,而對于Weak引用對象,GC總是進行回收。Weak引用對象更容易、更快被 GC回收。雖然,GC在運作時一定回收Weak對象,但是複雜關系的Weak對象群常常需要好幾次GC的運作才能完成。Weak引用對象常常用于Map結構中,引用資料量較大的對象,一旦該對象的強引用為null時,GC能夠快速地回收該對象空間。

Phantom引用的用途較少,主要用于輔助 finalize函數的使用。Phantom對象指一些對象,它們執行完了finalize函數,并為不可達對象,但是它們還沒有被GC回收。這種對象可以輔助finalize進行一些後期的回收工作,我們通過覆寫Reference的clear()方法,增強資源回收機制的靈活性。

一些Java編碼的建議

根據GC的工作原理,我們可以通過一些技巧和方式,讓GC運作更加有效率,更加符合應用程式的要求。以下就是一些程式設計的幾點建議。

1.最基本的建議就是盡早釋放無用對象的引用。大多數程式員在使用臨時變量的時候,都是讓引用變量在退出活動域(scope)後,自動設定為null.我們在使用這種方式時候,必須特别注意一些複雜的對象圖,例如數組,隊列,樹,圖等,這些對象之間有互相引用關系較為複雜。對于這類對象,GC回收它們一般效率較低。如果程式允許,盡早将不用的引用對象賦為null.這樣可以加速GC的工作。

2.盡量少用finalize函數。finalize函數是Java提供給程式員一個釋放對象或資源的機會。但是,它會加大GC的工作量,是以盡量少采用finalize方式回收資源。

3.如果需要使用經常使用的圖檔,可以使用soft應用類型。它可以盡可能将圖檔儲存在記憶體中,供程式調用,而不引起OutOfMemory.

4.注意集合資料類型,包括數組,樹,圖,連結清單等資料結構,這些資料結構對GC來說,回收更為複雜。另外,注意一些全局的變量,以及一些靜态變量。這些變量往往容易引起懸挂對象(dangling reference),造成記憶體浪費。