Java垃圾回收機制

Java垃圾回收機制

  說到垃圾回收(Garbage Collection,GC),很多人就會自然而然地把它和Java聯繫起來。在Java中,程序員不需要去關心內存動態分配和垃圾回收的問題,這一切都交給了JVM來處理。顧名思義,垃圾回收就是釋放垃圾佔用的空間,那麼在Java中,什麼樣的對象會被認定爲“垃圾”?那麼當一些對象被確定爲垃圾之後,採用什麼樣的策略來進行回收(釋放空間)?在目前的商業虛擬機中,有哪些典型的垃圾收集器?下面我們就來逐一探討這些問題。以下是本文的目錄大綱:

  一.如何確定某個對象是“垃圾”?

  二.典型的垃圾收集算法

  三.典型的垃圾收集器

  如果有不正之處,希望諒解和批評指正,不勝感激。

  請尊重作者勞動成果,轉載請標明原文鏈接:

   http://www.cnblogs.com/dolphin0520/p/3783345.html

一.如何確定某個對象是“垃圾”?

  在這一小節我們先了解一個最基本的問題:如果確定某個對象是“垃圾”?既然垃圾收集器的任務是回收垃圾對象所佔的空間供新的對象使用,那麼垃圾收集器如何確定某個對象是“垃圾”?—即通過什麼方法判斷一個對象可以被回收了。

  在java中是通過引用來和對象進行關聯的,也就是說如果要操作對象,必須通過引用來進行。那麼很顯然一個簡單的辦法就是通過引用計數來判斷一個對象是否可以被回收。不失一般性,如果一個對象沒有任何引用與之關聯,則說明該對象基本不太可能在其他地方被使用到,那麼這個對象就成爲可被回收的對象了。這種方式成爲引用計數法

  這種方式的特點是實現簡單,而且效率較高,但是它無法解決循環引用的問題,因此在Java中並沒有採用這種方式(Python採用的是引用計數法)。看下面這段代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public class Main {

    public static void main(String[] args) {

        MyObject object1 = new MyObject();

        MyObject object2 = new MyObject();

         

        object1.object = object2;

        object2.object = object1;

         

        object1 = null;

        object2 = null;

    }

}

 

class MyObject{

    public Object object = null;

}

  最後面兩句將object1和object2賦值爲null,也就是說object1和object2指向的對象已經不可能再被訪問,但是由於它們互相引用對方,導致它們的引用計數都不爲0,那麼垃圾收集器就永遠不會回收它們。

  爲了解決這個問題,在Java中採取了可達性分析法。該方法的基本思想是通過一系列的“GC Roots”對象作爲起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定爲不可達的對象不一定就會成爲可回收對象。被判定爲不可達的對象要成爲可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成爲可回收對象的可能性,則基本上就真的成爲可回收對象了。

  至於可達性分析法具體是如何操作的我暫時也沒有看得很明白,如果有哪位朋友比較清楚的話請不吝指教。

  下面來看個例子:

1

2

3

4

5

6

7

Object aobj = new Object ( ) ;

Object bobj = new Object ( ) ;

Object cobj = new Object ( ) ;

aobj = bobj;

aobj = cobj;

cobj = null;

aobj = null;

   第幾行有可能會使得某個對象成爲可回收對象?第7行的代碼會導致有對象會成爲可回收對象。至於爲什麼留給讀者自己思考。

  再看一個例子:

1

2

3

String str = new String("hello");

SoftReferencesr = new SoftReference(new String("java"));

WeakReferencewr = new WeakReference(new String("world"));

  這三句哪句會使得String對象成爲可回收對象?第2句和第3句,第2句在內存不足的情況下會將String對象判定爲可回收對象,第3句無論什麼情況下String對象都會被判定爲可回收對象。

  最後總結一下平常遇到的比較常見的將對象判定爲可回收對象的情況:

  1)顯示地將某個引用賦值爲null或者將已經指向某個對象的引用指向新的對象,比如下面的代碼:

1

2

3

4

5

Object obj = new Object();

obj = null;

Object obj1 = new Object();

Object obj2 = new Object();

obj1 = obj2;

    2)局部引用所指向的對象,比如下面這段代碼:

1

2

3

4

5

6

7

8

void fun() {

 

.....

    for(int i=0;i<< code="">10;i++) {

        Object obj = new Object();

        System.out.println(obj.getClass());

    }   

}

   循環每執行完一次,生成的Object對象都會成爲可回收的對象。

  3)只有弱引用與其關聯的對象,比如:

1

WeakReferencewr = new WeakReference(new String("world"));

二.典型的垃圾收集算法

  在確定了哪些垃圾可以被回收後,垃圾收集器要做的事情就是開始進行垃圾回收,但是這裏面涉及到一個問題是:如何高效地進行垃圾回收。由於Java虛擬機規範並沒有對如何實現垃圾收集器做出明確的規定,因此各個廠商的虛擬機可以採用不同的方式來實現垃圾收集器,所以在此只討論幾種常見的垃圾收集算法的核心思想。

  1.Mark-Sweep(標記-清除)算法

  這是最基礎的垃圾回收算法,之所以說它是最基礎的是因爲它最容易實現,思想也是最簡單的。標記-清除算法分爲兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。具體過程如下圖所示:

\"\"

  從圖中可以很容易看出標記-清除算法實現起來比較容易,但是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會導致後續過程中需要爲大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。

  2.Copying(複製)算法

  爲了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。具體過程如下圖所示:

\"\"

  這種算法雖然實現簡單,運行高效且不容易產生內存碎片,但是卻對內存空間的使用做出了高昂的代價,因爲能夠使用的內存縮減到原來的一半。

  很顯然,Copying算法的效率跟存活對象的數目多少有很大的關係,如果存活對象很多,那麼Copying算法的效率將會大大降低。

  3.Mark-Compact(標記-整理)算法

  爲了解決Copying算法的缺陷,充分利用內存空間,提出了Mark-Compact算法。該算法標記階段和Mark-Sweep一樣,但是在完成標記之後,它不是直接清理可回收對象,而是將存活對象都向一端移動,然後清理掉端邊界以外的內存。具體過程如下圖所示:

\"\"

  

  4.Generational Collection(分代收集)算法

  分代收集算法是目前大部分JVM的垃圾收集器採用的算法。它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不同的區域。一般情況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特點是每次垃圾收集時只有少量對象需要被回收,而新生代的特點是每次垃圾回收時都有大量的對象需要被回收,那麼就可以根據不同代的特點採取最適合的收集算法。

  目前大部分垃圾收集器對於新生代都採取Copying算法,因爲新生代中每次垃圾回收都要回收大部分對象,也就是說需要複製的操作次數較少,但是實際中並不是按照1:1的比例來劃分新生代的空間的,一般來說是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另一塊Survivor空間中,然後清理掉Eden和剛纔使用過的Survivor空間。

  而由於老年代的特點是每次回收都只回收少量對象,一般使用的是Mark-Compact算法。

  注意,在堆區之外還有一個代就是永久代(Permanet Generation),它用來存儲class類、常量、方法描述等。對永久代的回收主要回收兩部分內容:廢棄常量和無用的類。

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