Java垃圾回收機制

垃圾回收

垃圾回收回調(diào)方法:

  • finalize()函數(shù)是在JVM回收內(nèi)存時執(zhí)行的,但JVM并不保證在回收內(nèi)存時一定會調(diào)用finalize()。

JVM的垃圾回收機制:

  • 在內(nèi)存充足的情況下,顯式調(diào)用System.gc()(system.gc調(diào)用僅僅是建議虛擬機進行回收,并不一定馬上會進行gc)
  • 在內(nèi)存不足的情況下,垃圾回收將自動運行

對象狀態(tài)

  • 可達狀態(tài):
    有一個以上的引用變量引用此對象

  • 可恢復狀態(tài):
    如果程序中某個對象不再有任何的引用變量引用它,它將先進入可恢復狀態(tài),系統(tǒng)的垃圾回收機制準備回收該對象的所占用的內(nèi)存,在回收之前,系統(tǒng)會調(diào)用finalize()方法進行資源清理,如果資源整理后重新讓一個以上引用變量引用該對象,則這個對象會再次變?yōu)榭蛇_狀態(tài),否則就會進入不可達狀態(tài)。

  • 不可達狀態(tài):
    當對象的所有關聯(lián)都被切斷,且系統(tǒng)調(diào)用finalize()方法進行資源清理后依舊沒有使該對象變?yōu)榭蛇_狀態(tài),則這個對象將永久性失去引用并且變成不可達狀態(tài),系統(tǒng)才會真正的去回收該對象所占用的資源。

引用

級別: 強引用 > 軟引用 > 弱引用 > 虛引用

StrongReference

默認引用實現(xiàn),當沒有任何對象指向它時,GC執(zhí)行后將會被回收

Food food = new Food();
food = null;

WeakReference

所引用的對象在JVM內(nèi)不再有強引用時,GC執(zhí)行后將會被回收

處理過程:

  • WeakReference對象的referent域被設置為null,從而使該對象不再引用heap對象。
  • WeakReference引用過的heap對象被聲明為finalizable。
  • 同時或者一段時間后WeakReference對象被添加到它的ReferenceQueue(如果ReferenceQueue存在的話)。

對于軟引用和弱引用,入隊和finalize方法的執(zhí)行是沒有固定順序的

 Food food = new Food();
 WeakReference<Food> weakFood = new      WeakReference<Food>(food);
 food = null;

使用:隨時取得某對象的信息(不影響此對象的垃圾收集)

A obj = new A();
WeakReference wr = new WeakReference(obj);
obj = null;
//等待一段時間,obj對象就會被垃圾回收
...
if (wr.get()==null) {
  System.out.println("obj 已經(jīng)被清除了 ");} else {
  System.out.println("obj 尚未被清除,其信息是 "+obj.toString());
}

SoftReference

類似WeakReference,但SoftReference會盡可能長的保留引用直到JVM內(nèi)存不足時才會被回收, 適合緩存應用

處理過程同WeakReference

Food food = new Food();
SoftReference<Food> softFood = new  SoftReference<Food>(food);
food = null;
// JVM OutOfMemory

使用:簡單對象cache

A obj = new A();
SoftRefenrence sr = new SoftReference(obj);
//引用時
if(sr!=null){
   obj = sr.get();
}else{
  obj = new A();
  sr = new SoftReference(obj);
}

PhantomReference

跟蹤referent何時被enqueue到ReferenceQueue中,它唯一的目的就是對象被回收時能收到一個通知,用于追蹤對象被垃圾回收的狀態(tài),需要和引用隊列ReferenceQueue類聯(lián)合使用。

不建議使用,有潛在的內(nèi)存泄露風險,因為JVM不會自動幫助我們釋放,我們必須要保證它指向的堆對象是不可達的

虛引用帶來的內(nèi)存泄露風險參考:java中虛引用PhantomReference與弱引用WeakReference(軟引用SoftReference)的差別

軟引用和弱引用差別不大,JVM都是先將其referent字段設置成null,之后將軟引用或弱引用,加入到關聯(lián)的引用隊列中。我們可以認為JVM先回收堆對象占用的內(nèi)存,然后才將軟引用或弱引用加入到引用隊列。

而虛引用則不同,JVM不會自動將虛引用的referent字段設置成null,而是先保留堆對象的內(nèi)存空間,直接將PhantomReference加入到關聯(lián)的引用隊列,也就是說如果我們不手動調(diào)用PhantomReference.clear(),虛引用指向的堆對象內(nèi)存是不會被釋放的。

處理過程:

  • 不把referent設置為null.
  • PhantomReference引用過的heap對象處理到finalized狀態(tài),即調(diào)用了finalize()方法.
  • heap對象被釋放之前把PhantomRefrence對象添加到它的ReferenceQueue中.

摘錄部分蓋樓評論:
我覺得其實是這樣,其實GC做的工作分成是兩部分,第一部分是將對象從finalizable狀態(tài)到finalized狀態(tài),這只是完成了資源的釋放;第二部分是reclaimed對象占用的內(nèi)存。其實所有在ref中的三種reference的referent的對象的reclaimed都只有在相應reference對象的clear方法調(diào)用之后,才能進行,所以,GC只是保證weakreference、softreference的clear方法被GC自動調(diào)用,并被加到referencequeue中,但是phantomreference對象在被加入到referencequeue中之前對象就已經(jīng)被GC finalied(如果定義了finalize方法的話,我所指的finalized是指調(diào)用了finalize方法)了,只是還沒有進行第二步reclaimed,因為phantomreference對象的clear方法還沒有被調(diào)用,所以不能進行reclaimed。

  Food food = new Food();
  PhantomReference<Food> phantomFood = new PhantomReference<Food>(food, new ReferenceQueue<Food>()); 
  food = null;

使用:

  • Object的finalize方法在回收之前調(diào)用,若在finalize方法內(nèi)創(chuàng)建此類的強引用會導致對象無法回收,可能引發(fā)JVM OutOfMemory
  • PhantomReference在finalize方法執(zhí)行后進行回收,避免上述問題

??參考網(wǎng)頁關于虛引用PhantomReference

@SuppressWarnings("static-access")
public static void main(String[] args) throws Exception {
    String abc = new String("abc");
    System.out.println(abc.getClass() + "@" + abc.hashCode());
    final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
    new Thread() {
        public void run() {
            while (isRun) {
                Object obj = referenceQueue.poll();
                if (obj != null) {
                    try {
                        Field rereferent = Reference.class
                                .getDeclaredField("referent");
                        rereferent.setAccessible(true);
                        Object result = rereferent.get(obj);
                        System.out.println("gc will collect:"
                                + result.getClass() + "@"
                                + result.hashCode() + "\t"
                                + (String) result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }.start();
    PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
            referenceQueue);
    abc = null;
    Thread.currentThread().sleep(3000);
    System.gc();
    Thread.currentThread().sleep(3000);
    isRun = false;
}

總結

  • 強引用指向的對象如果被引用,發(fā)生GC時是不會被回收的,除非該對象沒有被引用
  • 軟引用在內(nèi)存非常緊張的時候會被回收(無引用),其他時候不會被回收,所以在使用之前要判斷是否為null從而判斷他是否已經(jīng)被回收了
  • 弱引用和虛引用指向的對象(無引用)在發(fā)生GC時一定會被回收
  • 通過虛引用得不到引用的對象實例,虛引用的get()方法永遠返回null

參考

Java的內(nèi)存回收機制
Java中三個引用類SoftReference 、 WeakReference 和 PhantomReference的區(qū)別
Java引用
引用
Java幽靈引用的作用

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 1. 垃圾回收的意義在C++中,對象所占的內(nèi)存在程序結束運行之前一直被占用,在明確釋放之前不能分配給其它對象;而在...
    愛情小傻蛋閱讀 953評論 0 11
  • 來自: Android夢想特工隊作者: Aaron主頁: http://www.wxtlife.com/原...
    技術特工隊閱讀 4,415評論 0 28
  • 1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,889評論 17 311
  • 一、垃圾回收機制的意義Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程序員最頭疼的內(nèi)存管理的問題迎刃...
    任任任任師艷閱讀 663評論 0 0
  • 1. 我和妻子結婚的第二個月,她就忍受不了在家平靜的生活,非要拉著我去外面走走。我正在公司加班,短信問她,要去哪里...
    陳汐年閱讀 782評論 10 24