JAVA常用類--Cleaner類

月亮警察發表於2024-03-11

Cleaner類

注意:在JDK1.9以上版本可使用

在Java程式中提供有GC的垃圾回收機制,如果發現堆記憶體不足時一定要進行垃圾回收以釋放記憶體空間

但如果某些物件在回收前需要做一些處理,可以透過覆寫Object類中的finalize()方法來實現這種回收前的處理

  • finalize()方法的定義:
@Deprecated(since="9")
protected void finalize() throws Throwable { }

finalize()方法實際上從JDK1.0時就開始提供,但一直到JDK1.9後才發現此方法成為了不推薦使用的方法,同時這個方法上所丟擲的一個Throwabke型別的異常(Erroe和Exception),在物件回收時可能會出現各種問題,但不影響回收!

例項1:傳統的物件回收

  • 不進行手動回收
class Book{
    //無參構造
    public Book(){
        System.out.println("【構造】用心編寫了一本優秀的原創技術圖書!");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("【析構】圖書使用完畢,可以銷燬!");
    }
}
public class Application {
    public static void main(String[] args) {
        Book book = new Book();//建立例項化物件
        book = null;//斷開堆記憶體的指向,變為垃圾空間
    }
}

執行結果如下:

【構造】用心編寫了一本優秀的原創技術圖書!
  • 呼叫gc()進行手動回收

如果不進行手動的gc()回收,則需要等待自動回收,自動回收的時間是不可控的!!

class Book{
    //無參構造
    public Book(){
        System.out.println("【構造】用心編寫了一本優秀的原創技術圖書!");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("【析構】圖書使用完畢,可以銷燬!");
    }
}
public class Application {
    public static void main(String[] args) {
        Book book = new Book();//建立例項化物件
        book = null;//斷開堆記憶體的指向,變為垃圾空間
        //如果不進行手動的gc()回收,則需要等待自動回收,自動回收的時間是不可控的!!
        System.gc();//進行垃圾回收
    }
}

執行結果如下:

【構造】用心編寫了一本優秀的原創技術圖書!
【析構】圖書使用完畢,可以銷燬!
  • 手動丟擲異常

finalize()方法程式碼修改如下:(其他程式碼不變)

protected void finalize() throws Throwable {
        System.out.println("【析構】圖書使用完畢,可以銷燬!");
        throw new Exception("這本圖書還有用,不能銷燬!!");//手動丟擲異常
    }

執行結果如下:

【構造】用心編寫了一本優秀的原創技術圖書!
【析構】圖書使用完畢,可以銷燬!

以上的這種程式做法是在JDK1.9以前提供的處理形式,但這樣的做法一直以來都存在嚴重問題

如果在finalize()裡面出現一些執行緒的死鎖操作,那麼就可能會造成垃圾回收的失敗,同時也會產生嚴重的執行緒阻塞問題

解決方法:在JDK1.9之後,啟動了一個專屬的回收執行緒----Cleaner類

例項2:

import sun.misc.Cleaner;
class Book implements Runnable{ //設計一個回收執行緒
    //無參構造
    public Book(){
        System.out.println("【構造】用心編寫了一本優秀的原創技術圖書!");
    }
    public void read(){
        System.out.println("【讀書】認真學習!");
    }
    @Override
    public void run() { //真正的回收由執行緒來完成!!!
        System.out.println("【析構】圖書使用完畢,可以銷燬!");
    }
}
class BookCleaner implements AutoCloseable{ //必須實現AutoCloseable介面
    private static final Cleaner cleaner = Cleaner.create();//建立一個回收物件
    private Cleaner.Cleanable cleanable;
    public BookCleaner(Book book){
        this.cleanable = cleaner.register(this,book);//註冊一個回收執行緒
    }

    @Override
    public void close() throws Exception {
        this.cleanable.clean();//釋放時進行垃圾清除
    }
}
public class Application {
    public static void main(String[] args) {
        Book book = new Book();
        try(BookCleaner bc = new BookCleaner()){
            book.read();//可以在中間進行一些物件的處理操作
        }catch (Exception e){}
    }
}

執行結果如下:

【構造】用心編寫了一本優秀的原創技術圖書!
【讀書】認真學習!
【析構】圖書使用完畢,可以銷燬!

圖解

經過以上的分析基本已經清楚物件的建立以及回收處理的操作,以下是對Java中物件的生命週期流程

  1. 建立階段:每當使用關鍵字new就表示要開闢新的堆記憶體空間,同時每一個新的物件例項化時都需要去執行類中的構造方法,構造方法的目的是為了類中成員屬性的初始化
  2. 應用階段:利用指定的物件名稱可以直接進行類之中的方法的呼叫處理
  3. 不可見階段:如果現實某一個方法內部有一個物件,則該方法執行完畢後該物件將不再使用
  4. 不可達階段:某一塊堆記憶體已經不再有任何的棧記憶體所指向,那麼這塊空間將成為垃圾空間
  5. 收集階段:JVM會自動的進行此塊垃圾空間的標記,標記之後將準備透過GC回收釋放,JDK1.8及以前的版本均使用finalize()方法,JKD1.9及以後的版本推薦使用CLeaner來完成
  6. 釋放階段:JVM重新回收垃圾的堆記憶體空間,供後續新物件使用

相關文章