1. 程式人生 > >深入理解JVM:垃圾收集器與內存分配策略

深入理解JVM:垃圾收集器與內存分配策略

四種 內存回收 第一次 不可達 append test 方法 static hot

堆裏面存放著Java世界差點兒全部的對象實例,垃圾收集器在對堆進行回收前。第一件事情就是要確定這些對象之中哪些還存活,哪些已經死去。推斷對象的生命周期是否結束有下面幾種方法
引用計數法
詳細操作是給對象加入一個引用計數器。每當有一個地方引用時。計數器的值就加1,;當引用失效時。計數器就減1。不論什麽時刻計數器為0的對象就 是不可能再被使用的。客觀的說引用計數器算法實現簡單,判定效率也非常高,在大部分情況下他都是一個不錯的算法。可是引用計數器有缺陷
舉個簡單的樣例,對象A和對象B都有字段instance,賦值命令objA.instance = objB,objB.instance=objA,除此之外。這兩個對象再無不論什麽引用,實際上這兩個對象已經不可能再被訪問了,可是他們由於互相引用這對方。導致引用數據器不為0,無法通知CG收集器收集他們。

public class ReferenceCountingGC{
    public Object instance = null;
    private static final int _1MB = 1024*1024;
    // 這個成員屬性的唯一意義就是占點內存,以便能在GC日誌中看清楚是否被回收過
    private byte[] bigSize = new byte[2*_1MB];
    public static void testGC{
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new
ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; obj.A = null; obj.B = null; } }

通過GC日誌。虛擬機並沒有由於這兩個對象互相引用就不回收他們。這從側面說明虛擬機不是通過引用計數器來推斷對象是否存活。
可達性分析算法
這個算法的基本思路是通過一系列稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索。搜索走過的路徑稱為引用鏈,當一個對象到GC Roots沒有不論什麽引用鏈相連。則證明此對象是不可用的。
Java語言中。可作為GC Roots的對象包括
1、虛擬機棧(棧幀中的本地變量表)中引用的變量
2、方法區中類靜態屬性引用的對象
3、方法區中常量引用的對象
4、本地方法棧中JNI(Native方法)引用的對象
引用分析


假設reference類型的數據中存儲的數值代表的是還有一塊內存的起始地址,就稱為這塊內存代表著一個引用。這樣的定義太過狹隘,一個對象在這樣的定義下僅僅有被引用和沒有被引用兩種狀態。對於怎樣描寫敘述一些“食之無味。棄之可惜”的對象就顯得無能為力。我們希望能描寫敘述這樣一類對象,當內存空間足夠時。則能夠保存在內存之中。假設內存空間在進行垃圾回收後還是非常緊張,則能夠拋棄這些對象。
在JDK1.2以後,Java對引用進行了擴展。將引用分為強引用、軟引用、弱引用、虛引用四種,強度依次遞減。
1、強引用:程序中普遍存在。比如Object obj = new Object();僅僅要強引用來,CG永遠不會回收
2、軟引用:描寫敘述一些還實用,可是並不是必要的對象。

對於軟引用的對象。在系統將要發生內存溢出之前,會把這些對象列進回收範圍之中進行二次回收,假設回收了還沒有足夠的內存將報出內存溢出,JDK1.2之後提供了SoftReference類來實現軟引用。


3、弱引用:也是描寫敘述非必須對象,被弱引用的對象僅僅能生存到下一次垃圾收集之前。當垃圾收集器工作時,不管內存是否夠用都會被回收,JDK1.2之後提供了WeakReference類來實現弱引用。
4、虛引用:幽靈引用或者幻影引用,最弱。一個對象是否有虛無引用的存在。全然不會對其生存時間構成影響,也無法通過虛無引用來取得一個對象實例。
生存還是死亡
即使在可達性分析算法中不可達對象,也並不是是“非死不可”的,這時候他們臨時出“緩刑”之中,要真正宣告一個對象死亡。至少要經歷兩次標記過程:假設對象在進行可達性分析後發現沒有與GC Roots相連接的引用鏈,那他會被第一次標記而且進行一次篩查,篩查的條件是此對象是有有必要運行finalize方法。

當對象沒有覆蓋finalize方法,或者finalize方法已經被虛擬機調用過,虛擬機將這兩種情況視為沒有必要運行。


類的Finalize方法,能夠告訴垃圾回收器應該運行的操作,該方法從Object類繼承而來。在從堆中永久刪除對象之前。垃圾回收器調用該對象的Finalize方法。註意,無法確切地保證垃圾回收器何時調用該方法,也無法保證調用不同對象的方法的順序。即使一個對象包括還有一個對象的引用。或者在釋放一個對象非常久曾經就釋放了還有一個對象,也可能會以隨意的順序調用這兩個對象的Finalize方法。假設必須保證採用特定的順序,則必須提供自己的特有清理方法。
回收方法區
方法區即為Hotspot虛擬機中的永久代,永久代的垃圾收集主要回收兩部分內容:廢棄常量和沒用的類。以常量字符串”abc”為例,已經進入常量池,可是當前系統沒有不論什麽一個String對象叫做abc,換句話說就是沒有不論什麽String對象引用常量池中的abc,假設此時發生內存回收,這個abc常量就會被系統清理出常量池。常量池中的其它類、接口、方法、字段的符號引用也與此相似。


判定一個常量是否是廢棄常量比較簡單,而判定一個類是否是沒用的類的條件 要苛刻的多,沒用的類要滿足下面條件:
1、該類全部的實例都已經被回收,Java堆中不存在該類的不論什麽實例。
2、載入該類的ClassLoader已經被回收
3、該類的Java.lang.class對象沒有在不論什麽地方被引用。無法在不論什麽地方通過反射訪問該類。

深入理解JVM:垃圾收集器與內存分配策略