# JVM 垃圾回收-01 可達性分析與強弱軟虛引用詳解

可達型分析

可達性分析的理論

基本思路:
通過一系列GC Roots的根對象作為起始節點集, 根據引用關系向下搜索, 
搜索過程中走過的路徑稱為引用鏈,所有在這個引用鏈上的對象都是可達對象,
而其他的沒有與GC Roots根對象關聯的單獨存在的引用鏈上的對象則為不可達對象

圖示:


image
在內存中的場景:
上圖的 object1 中有字段object2 , object3 的引用, 
當我們把object1的引用ref賦值為null, 
那么object1就變為了 上圖右側的不可達對象的圖例了..需要被gc回收掉了,如下圖引用
image
代碼實現舉例子:


@Data
public class Test {
    public Test instance;
    public int i;
    public Test(int i) {
        this.i = i;
    }
}


public static void main(String[] args) {
    Test test0 = new Test(0);
    Test test1 = new Test(1);
    Test test2 = new Test(2);
    test0.instance=test1;
    test1=test2;
    System.out.println(test0.getInstance().getI());
}

執行結果 1
為什么不是2呢,test1已經被test2賦值了呀!
這是因為引用變量只能是指向某個對象的,而不能是指向引用的
所以test0.instance 指向的是test1在堆中的對象,
所以在后面改變了test1的指向時, 并沒有影響之前的指向.


public static void main(String[] args) {
    Test test0 = new Test(0);
    Test test1 = new Test(1);
    Test test2 = new Test(2);
    test0.instance=test1;
    test1.instance=test2;
    test0=null;
}

ps: test0=null則 test0的原映射對象是不在引用鏈上了,會被gc回收.
且test0.instance的由上例子可知是引用的對象test1, 但這里不會回收test1,
因為test1也自己定義了一個根對象, 所以test1還是在test1引用鏈上,但不在test0的引用鏈上了,
如果將test1=null也這樣設置,那么test1也將會被gc回收;

問題:那么在平時的代碼中我們要不要在使用后對象就將對象置空呢?
這個問題要看 JVM 垃圾回收-判斷對象是否可以回收 中的描述了

可達性根枚舉對象

GC Roots的對象
1. 虛擬機棧中的引用的對象            如:Object o = new Object(); o即為虛擬棧中的引用對象
2. 方法區中類靜態屬性引用的對象       如:public static String static_str="111";  static_str 即為引用對象
3. 方法區中常量引用的對象
4. 本地方法棧中 JNI(native方法)引用的對象
5. 被同步鎖synchronize持有的對象

ps:這里提到了很多引用,我們下面將詳細描述引用的分類,引用之所以進行設定不同的分類, 
   是因為在內存中數據的要求是多樣的,
   比如 我們希望在內存充足的情況下,保留這些類,但內存不足的時候,就進行回收(緩存機制)

引用是如何分類

強引用

使用方法

正常的寫的java代碼都是強引用99.999%
Object o = new Object();

這種就是強引用了,是不是在代碼中隨處可見,最親切。 
只要某個對象有強引用與之關聯,這個對象永遠不會被回收
即使內存不足,JVM寧愿拋出OOM,也不會去回收

回收的方法:

o = null;

示例:

我們需要新寫一個類,然后重寫finalize方法

public class Student {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Student 被回收了");
    }
}

public static void main(String[] args) {
        Student student = new Student();
        student = null;
        System.gc();
}

運行結果:
Student 被回收了

注釋:finalize方法是當gc時,系統來調用該方法
釋放該對象在堆中占用的內存. 該方法在Object中

軟引用

使用方法

用java.lang.ref.SoftReference 進行包裝
SoftReference<Student>studentSoftReference=new SoftReference<Student>(new Student());

回收方法

gc自動處理
當內存不足,會觸發JVM的GC,如果GC后,內存還是不足,就會把軟引用的包裹的對象給干掉
也就是只有在內存不足,JVM才會回收該對象

示例:

修改idea的堆內存大小 -Xmx20m  最大堆內存為20m , 設置內存追蹤 -XX:+PrintGCDetails

  SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]);
  System.out.println(softReference.get());
  System.gc();
  System.out.println(softReference.get());
  byte[] bytes = new byte[1024 * 1024 * 10];
  System.out.println(softReference.get());
  
  創建一個軟引用對象,里面包裹了byte[],byte[]占用了10M,然后又創建了10Mbyte[]
  
結果:

[GC (Allocation Failure)  5632K->1393K(19968K), 0.0049815 secs]
[B@763d9750
[GC (System.gc())  16187K->12252K(19968K), 0.0014227 secs]
[Full GC (System.gc())  12252K->11862K(19968K), 0.0101228 secs]
[B@763d9750
[GC (Allocation Failure)  12032K->11926K(19968K), 0.0004047 secs]
[GC (Allocation Failure)  11926K->11926K(19968K), 0.0002842 secs]
[Full GC (Allocation Failure)  11926K->11784K(19968K), 0.0049854 secs]
[GC (Allocation Failure)  11784K->11784K(19968K), 0.0002981 secs]
[Full GC (Allocation Failure)  11784K->1495K(16896K), 0.0066588 secs]
null
 
分析: 當gc回收時, 軟 

注釋:一般是緩存使用

弱引用

使用方法

java.lang.ref.WeakReference包裝
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);
System.out.println(weakReference.get());

回收方式

System.gc();
不管內存是否足夠,只要發生GC,都會被回收

示例

WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1]);
System.out.println(weakReference.get());
System.gc();
System.out.println(weakReference.get());

結果:
[GC (Allocation Failure)  5632K->1410K(19968K), 0.0016663 secs]
[B@763d9750
[GC (System.gc())  5726K->2034K(19968K), 0.0015112 secs]
[Full GC (System.gc())  2034K->1914K(19968K), 0.0113147 secs]
null

注釋:弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap。

虛引用

使用方法:

 當發生GC,虛引用就會被回收,并且會把回收的通知放到ReferenceQueue中
 ReferenceQueue queue = new ReferenceQueue();
 PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], queue);
 System.out.println(reference.get());
 
 作用時,當gc回收的時候會產生一條記錄到ReferenceQueue中

回收方式:

System.gc() 自動回收

示例:

@Data
@Builder
public class Student {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Student 被回收了");
    }
}


public static void main(String[] args) {
        ReferenceQueue queue = new ReferenceQueue();
        List<byte[]> bytes = new ArrayList<>();
        PhantomReference<Student> reference = new PhantomReference<Student>(new Student(),queue);
        new Thread(() -> {
            for (int i = 0; i < 100;i++ ) {
                bytes.add(new byte[1024 * 1024]);
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference poll = queue.poll();
                if (poll != null) {
                    System.out.println("虛引用被回收了:" + poll);
                }
            }
        }).start();
        Scanner scanner = new Scanner(System.in);
        scanner.hasNext();
    }

結果: 

[GC (Allocation Failure)  5632K->1372K(19968K), 0.0011861 secs]
[GC (Allocation Failure)  7004K->2185K(19968K), 0.0017764 secs]
[GC (Allocation Failure)  7205K->6394K(19968K), 0.0028557 secs]
[GC (Allocation Failure)  11653K->11554K(19968K), 0.0027832 secs]
[Full GC (Ergonomics)  11554K->11374K(19968K), 0.0134628 secs]
[Full GC (Ergonomics)  16624K->16456K(19968K), 0.0055213 secs]
[Full GC (Ergonomics)  18620K->18488K(19968K), 0.0098597 secs]
[Full GC (Allocation Failure)  18488K->18402K(19968K), 0.0115410 secs]
Student 被回收了
[Full GC (Ergonomics)  18949K->17924K(19968K), 0.0106182 secs]
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
    at com.logistic.JVMTest.lambda$main$0(JVMTest.java:16)
    at com.logistic.JVMTest$$Lambda$1/747464370.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
虛引用被回收了:java.lang.ref.PhantomReference@5eb93cd7

注釋:
第一個線程往集合里面塞數據,隨著數據越來越多,肯定會發生GC
第二個線程死循環,從queue里面拿數據,如果拿出來的數據不是null,就打印出來

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容