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