java 垃圾回收

轉載自http://www.cnblogs.com/200911/archive/2012/09/24/2700829.html

  1. 對象可能不被垃圾回收
  2. 垃圾回收並不等於“析構”
  3. 垃圾回收只與內存有關

對象在什麼時候回變成垃圾

Java中那些不可達的對象就會變成垃圾。那麼什麼叫做不可達?其實就是沒有辦法再引用到該對象了。主要有以下情況使對象變爲垃圾:

1.對非線程的對象來說,所有的活動線程都不能訪問該對象,那麼該對象就會變爲垃圾。

2.對線程對象來說,滿足上面的條件,且線程未啓動或者已停止。

例如:
(1)改變對象的引用,如置爲null或者指向其他對象。
Object x=new Object();//object1
Object y=new Object();//object2
x=y;//object1 變爲垃圾
x=y=null;//object2 變爲垃圾

(2)超出作用域
if(i==0){
Object x=new Object();//object1
}//括號結束後object1將無法被引用,變爲垃圾

(3)類嵌套導致未完全釋放
class A{
A a;
}
A x= new A();//分配一個空間
x.a= new A();//又分配了一個空間
x=null;//將會產生兩個垃圾

(4)線程中的垃圾
class A implements Runnable{
void run(){
//….
}
}
//main
A x=new A();//object1
x.start();
x=null;//等線程執行完後object1才被認定爲垃圾

二、垃圾回收機制

1.GC是什麼?

首先,垃圾回收機制是由垃圾收集器Garbage Collection(以下簡稱gc)來實現的。其實gc就是一個後臺的一個守護進程(其實這種說法不是很確切,因爲當內存不足的時候,gc會暫停應用程序),它的特別之處就是它是一個低優先級線程,但是可以根據內存的使用情況動態的調整它的優先級。因此,它是在內存中低到一定限度時纔會自動運行,從而實現對內存的回收。這就是垃圾回收的時間不確定的原因。

2. 爲什麼這樣設計呢?

因爲gc也是進程,也要消耗cpu等資源,如果gc執行過於頻繁會對java的程序的執行產生較大的影響(java解釋器本來就不快),因此JVM的設計者們選着了不定期的gc。

3. java爲什麼要用GC?

內存處理是時編程人員容易出現問題的地方,忘記或者錯誤的內存回收導致程序或者系統不穩定甚至奔潰,java提供的GC的功能使c++程序員最頭疼的內存管理問題迎刃而解,它使得java程序員在編寫程序的時候不需要在考慮內存管理,java GC會自動檢測對象是否超過作用域從而達到自動回收的內存的目的。

在垃圾回收器回收內存之前,還需要一些清理工作。Why?

因爲垃圾回收gc只能回收通過new關鍵字申請的內存(在堆上),但是堆上的內存並不完全是通過new申請分配的!

Java中通過JNI(Java Native Interface)可以訪問本地方法,一般是調用C,而C是通過malloc(分配存儲空間)和free(釋放存儲空間,存儲空間得不到釋放,就會造成內存泄露)進行申請內存的。這部分“特殊的內存”如果不手動釋放,就會導致內存泄露,gc是無法回收這部分內存的。

所以java類中定義一個名爲finalize的方法。它可以用來做一些清理工作。它的工作原理“假定”是這樣的:一旦JVM的垃圾回收期準備好釋放對象所暫用的內存空間,將首先調用其finalize方法(每個類都有,都繼承object類),並且在下一次垃圾回收動作時纔會真正回收對象所佔用的內存空間。

對上面的原理,很容易產生疑惑,如下:
1、gc是負責回收內存的,那finalize也是用來回收內存的,他們一樣嗎?
答:通過創建對象產生的內存,gc可以回收(至於如何回收,後面會講到)。但是有些特殊內存gc不能回收,因此可以在finalize中用本地方法(native method)如free操作等!

2、一個對象的finalize方法有什麼要注意的?
答:一個對象的finalize方法只能被調用一次!它並沒有減少垃圾回收器的工作量!執行finalize後,gc依然需要判斷這塊內存空間是否可以回收!

3、爲什麼sun不推薦用finalize方法來完成清理工作呢?

答:finalize方法只是在垃圾回收之前纔會被調用。但是我們知道gc本來就是不確定的,因此當一個對象變爲垃圾之後,我們並不知道垃圾回收器在何時運行,也就不知道finalize方法何時被調用了。有可能永遠都不被調用。(程序退出的時候,未回收的內存將直接交給操作系統)。由此可知,依靠finalize來完成清理工作是不靠譜的。正確的方法是在放棄這個對象之前調用相應的方法來進行內存的釋放!(顯式地調用)。

4、有人可能會問,顯式調用System.gc()強制運行gc不也可以調用到finalize完成清理嗎?
答:我認爲,不可取。因爲每次想執行清理都需要調用gc()來強制啓動垃圾處理器,這樣垃圾回收率低,反而很影響程序效率!爲何不將清理工作放到某個方法中,準備放棄該對象的內存空間之前就調用該方法!

如何回收內存?下面介紹幾種垃圾回收技術:

5、方法一:引用計數法。簡單但速度很慢。缺陷是:不能處理循環引用的情況。

用法如下:每個對象都含有一個引用計數器,當連接到對象的時候計數器就加1,當離開作用域,或者被置爲null時,引用技術器就減1。垃圾回收的時候釋放計數器爲0的內存空間。
如果出現循環引用,則會出現“對象應該被回收,但計數器不爲0”。

例如: a引用了b, b又反過來引用了a, 這時a,b就在堆上形成了一個閉環, 如果除b外只有c引用了a, 且只有a引用了b, 則c死亡時a,b也應該死掉, 但此時b還在引用着a, 於是a死不掉, a死不掉則b也死不掉,這就是所謂的循環引用問題。

在一些更快的模式中,垃圾回收並非基於引用記數技術。它依據的思想是:對任何“活的對象”,一定能最終追溯到其存活在堆棧或靜態存儲區之中的引用。由此,如果從堆棧上遍歷所有的引用,就能找到所有活的對象。

方法二:停止-複製(stop and copy)。效率低,需要的空間大,優點,不會產生碎片。

  它將可用內存容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊用完之後,就將還存活的對象複製到另外一塊上面,然後在把已使用過的內存空間一次清理掉。這樣使得每次都是對其中的一塊進行內存回收,不會產生碎片等情況,只要移動堆頂的指針,按順序分配內存即可,實現簡單,運行高效。

方法三:標記 - 清除算法 (mark and sweep)。速度較快,佔用空間少,標記清除後會產生大量的碎片。

用法如下:先暫停程序的運行,遍歷所有引用,每找到一個存活對象就給它一個標記,此過程中不進行回收。當標記結束後纔開始清理。清理後,剩下的部分是不連續的。如果希望等到連續的空間則需要進行復制。

JAVA虛擬機中是如何做的?

java的做法很聰明,我們稱之爲”自適應”的垃圾回收器,或者是”自適應的、分代的、停止-複製、標記-清掃”式垃圾回收器。它會根據不同的環境和需要選擇不同的處理方式。
java 虛擬機會進行監視,如果所有的對象都很穩定,垃圾回收器的效率降低,就會切換到“標記-清理”方式。同樣,java會跟蹤“標記-清理”的效果,要是堆空間中出現很多碎片,就會切換回“停止-複製”方式。

三、java中的內存泄漏

從泄漏的內存位置角度可以分爲兩種:JVM 中 Java Heap 的內存泄漏;JVM 內存中 native memory 的內存泄漏。

1、java heap的內存泄漏。

由於java heap的空間是在jvm過程中動態變化的。如果Java對象越來越多,佔據Java Heap的空間也越來越大,JVM會在運行時擴充Java Heap的容量。如果java heap容量擴充到上限,並且在GC後仍然沒有足夠的空間分配新的java對象,便會拋出out of memory異常,導致JVM進程崩潰。

Java Heap 中 out of memory 異常的出現有兩種原因——①程序過於龐大,致使過多Java對象的同時存在。②程序編寫的錯誤導致Java Heap內存泄漏。多種原因可能導致Java Heap內存泄漏。JNI編程錯誤也可能導致Java Heap的內存泄漏。

2、JVM 中 native memory 的內存泄漏

從操作系統角度看,JVM 在運行時和其它進程沒有本質區別。在系統級別上,它們具有同樣的調度機制,同樣的內存分配方式,同樣的內存格局。JVM進程空間中,Java Heap以外的內存空間稱爲JVM的native memory。 進程的很多資源都是存儲在JVM的native memory中,例如載入的代碼映像,線程的堆棧,線程的管理控制塊,JVM的靜態數據、全局數據等等。也包括JNI程序中native code分配到的資源。在JVM運行中,多數進程資源從native memory中動態分配。當越來越多的資源在 native memory中分配,佔據越來越多native memory空間並且達到native memory上限時,JVM會拋出異常,使JVM進程異常退出。而此時Java Heap往往還沒有達到上限。多種原因可能導致JVM的native memory內存泄漏。例如JVM在運行中過多的線程被創建,並且在同時運行。JVM 爲線程分配的資源就可能耗盡native memory的容量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章