JAVA虛擬機器學習筆記:JVM記憶體模型中記憶體回收方法

來源:互聯網
上載者:User
上篇文章介紹了JVM記憶體模型的相關知識,其實還有些內容可以更深入的介紹下,比如運行時常量池的動態插入,直接記憶體等,後期抽空再完善下上篇部落格,今天來介紹下JVM中的一些記憶體回收策略。

一、finailize()方法

在介紹GC策略前,先介紹下GC中的finailize方法。當對象沒有任何引用的時候,通常這個對象會被回收掉,但如果我們想在對象被回收前進行一些操作,比如關閉一些資源,或者讓這個對象複活,不讓他被回收怎麼辦?這時候就要用到finailize方法了。finailize方法是Object類中定義的方法,意味著任何一個對象都有這個方法。但這個方法只會調用一次,如果把這個對象複活後再次讓這個對象死亡,那第2次回收該對象的時候是不會調用finailize方法的,而且優先順序比較低,並不能保證一定會被執行,因此不建議使用finalize方法。總結起來就是3個特性: ①、GC之前被調用 。②、只會被調用一次。③、不可靠,不能保證被執行,不建議使用。關於finalize使用方法,參考如下代碼:

 1 public class FinalizeTest { 2  3     private static FinalizeTest test; 4     /** 5      * VM參數:-XX: +PrintGCDetails -Xmx=1M -Xms=1M 6      * 7      * @param args 8      */ 9     public static void main(String[] args) {10         //先對test對象賦值11         test = new FinalizeTest();12         int _1m = 1024 * 1024;13         //將test置為null,便於回收14         test = null;15         try {16             System.gc();17             //類比睡眠5s,finalize優先順序較低,保證finalize能執行18             Thread.sleep(5000);19         } catch (InterruptedException e) {20             e.printStackTrace();21         }22         if (test != null) {23             System.out.println("first,i am alive");24         }else{25             System.out.println("first,i am dead");26         }27         //由於test在finalize方法裡複活了,再次將test置為null28         test = null;29         try {30             System.gc();31             Thread.sleep(5000);//類比睡眠5s,讓GC回收32         } catch (InterruptedException e) {33             e.printStackTrace();34         }35         if (test != null) {36             System.out.println("second,i am alive");37         }else{38             System.out.println("second,i am dead");39         }40 41     }42     @Override43     protected void finalize() throws Throwable {44         test = this ;45         System.out.println("finalize excuted");46         super.finalize();   //調用父類的finailize方法47     }48 }

該代碼運行結果如下:

可以看到,finalize方法執行後,test對象又被重新啟用了,因此列印了first,i am alive。但是第二次GC的時候,finalize方法並未被執行,因此列印了second,i am dead。前面提到finalize是優先順序低不可靠的,那如果沒有Thread.sleep(5000),再來看下代碼和結果:

 1 public class FinalizeTest { 2  3     private static FinalizeTest test; 4     /** 5      * VM參數:-XX: +PrintGCDetails -Xmx=1M -Xms=1M 6      * 7      * @param args 8      */ 9     public static void main(String[] args) {10         //先對test對象賦值11         test = new FinalizeTest();12         int _1m = 1024 * 1024;13         //將test置為null,便於回收14         test = null;15         try {16             System.gc();17             //類比睡眠5s,finalize優先順序較低,保證finalize能執行18             //不執行睡眠操作,Thread.sleep(5000);19         } catch (Exception e) {20             e.printStackTrace();21         }22         if (test != null) {23             System.out.println("first,i am alive");24         }else{25             System.out.println("first,i am dead");26         }27         //由於test在finalize方法裡複活了,再次將test置為null28         test = null;29         try {30             System.gc();31             //不執行睡眠操作,Thread.sleep(5000);//類比睡眠5s,讓GC回收32         } catch (Exception e) {33             e.printStackTrace();34         }35         if (test != null) {36             System.out.println("second,i am alive");37         }else{38             System.out.println("second,i am dead");39         }40 41     }42     @Override43     protected void finalize() throws Throwable {44         test = this ;45         System.out.println("finalize excuted");46         super.finalize();   //調用父類的finailize方法47     }48 }

運行結果如下:

這裡可以很清楚地看到,finalize方法的優先順序是比較低的。

關於這個例子的反思:這個例子中第一段代碼是參考《深入理解java虛擬機器》裡的代碼實現的,但是總感覺有2點疑問:為什麼test對象是以static修飾的成員變數方式存在?如果是static修飾,那就是存在方法區了,而方法區的GC通常效果不太好的。另一個是以成員變數的方式存在,這樣finalize回收的時候,體現不出是對當前對象本身的回收,所以感覺這個例子並不是很好。

二、引用計數法

引用計數法是一種比較早的GC回收演算法,目前一般不採用,其主要思想是:每個對象都維持一個引用計數器,初始值為0,當一個對象被引用的時候,該對象的引用計數器就加1,當不被引用的時候,該對象的引用計數器就減1,如果一個對象的引用計數器變為了0,則該對象被認為是可以回收的。採用這種方式的優缺點都很明顯,優點是實現簡單,效率高,缺點是可能存在循環參考,導致記憶體溢出。

三、標記-清除法

標記-清除法按名字分為“標記”和“清除”2個階段,其基本思想是:首先標記出所有存活的對象,標記完成後,統一清除所有被標記的對象。那怎麼判斷某個對象是可以回收的呢?GC時,從一系列GC Roots根節點開始遍曆,遍曆時走過的路徑即稱為引用鏈,如果一個對象和GC Roots沒有任何引用鏈相關,那麼這個對象就不可用,就會被判定為可回收,這種演算法也叫根搜尋演算法。那麼哪些對象可以成為GC Roots對象呢?在java語言裡,可以作為GC Roots的對象包括下面4種:

虛擬機器棧中的引用變數

方法區中的類靜態屬性引用的對象

方法區中的常量引用的對象

本地方法棧中JNI(即native方法)的引用的對象

標記-清除法的演算法如下:

註:本文的GC回收演算法圖片轉自一個網友的文章(點這裡),該網友的圖片內容也與原著一致,只是顏色不同。

四、新生代的複製法

複製法的基本思想是:將記憶體分為大小相等的2塊,每次只使用其中一塊,GC時每次將所有存活的對象複製到另一塊地區,然後清理該記憶體

這幾種都是方法區和棧中的引用對象。複製法的優點是:實現簡單,回收速度快,且不會產生記憶體片段。但由於每次只使用其中一塊,導致記憶體利用率較低。複製演算法的如下:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.