【轉】深入理解JVM--JVM垃圾回收機制

陳俊成發表於2016-10-12

原文地址:點選進入
Java語言出來之前,大家都在拼命的寫C或者C++的程式,而此時存在一個很大的矛盾,C++等語言建立物件要不斷的去開闢空間,不用的時候有需要不斷的去釋放控制元件,既要寫建構函式,又要寫解構函式,很多時候都在重複的allocated,然後不停的~析構。於是,有人就提出,能不能寫一段程式在實現這塊功能,每次建立,釋放控制元件的時候複用這段程式碼,而無需重複的書寫呢?
1960年 基於MIT的Lisp首先提出了垃圾回收的概念,用於處理C語言等不停的析構操作,而這時Java還沒有出世呢!所以實際上GC並不是Java的專利,GC的歷史遠遠大於Java的歷史!
那究竟GC為我們做了什麼操作呢?

  1. 哪些記憶體需要回收?
  2. 什麼時候回收
  3. 如何回收

這時候有人就會疑惑了,既然GC已經為我們解決了這個矛盾,我們還需要學習GC麼?當然當然是肯定的,那究竟什麼時候我們還需要用到的呢?

  1. 排查記憶體溢位
  2. 排插記憶體洩漏
  3. 效能調優,排查併發瓶頸

我們知道,GC主要處理的是物件的回收操作,那麼什麼時候會觸發一個物件的回收的呢?

  • 物件沒有引用(容易理解)
  • 作用域發生未捕獲異常(怎麼理解?)
  • 程式在作用域正常執行完畢 (回收Java棧、Java堆)
  • 程式執行了System.exit()
  • 程式發生意外終止(被殺程式等)

其實,我們最容易想到的就是當物件沒有引用的時候會將這個物件標記為可回收物件,那麼現在就有一個問題,是不是這個物件被賦值為null以後就一定被標記為可回收物件了呢?我們來看一個例子:


package com.yhj.jvm.gc.objEscape.finalizeEscape;

import com.yhj.jvm.gc.objEscape.pojo.FinalizedEscapeTestCase;


/**
 * @Described:逃逸分析測試
 * @author YHJ create at 2011-12-24 下午05:08:09
 * @FileNmae com.yhj.jvm.gc.finalizeEscape.FinalizedEscape.java
 */
public class FinalizedEscape {
    public static void main(String[] args) throwsInterruptedException {
        System.out.println(FinalizedEscapeTestCase.caseForEscape);
       FinalizedEscapeTestCase.caseForEscape = newFinalizedEscapeTestCase();
        System.out.println(FinalizedEscapeTestCase.caseForEscape);
       FinalizedEscapeTestCase.caseForEscape=null;
       System.gc();
       Thread.sleep(100);
        System.out.println(FinalizedEscapeTestCase.caseForEscape);
    }
}
package com.yhj.jvm.gc.objEscape.pojo;
/**
 * @Described:逃逸分析測試用例
 * @author YHJ create at 2011-12-24 下午05:07:05
 * @FileNmae com.yhj.jvm.gc.pojo.TestCaseForEscape.java
 */
public class FinalizedEscapeTestCase {

    public static FinalizedEscapeTestCase caseForEscape = null;
    @Override
    protected void finalize() throws Throwable {
       super.finalize();
       System.out.println("哈哈,我已逃逸!");
       caseForEscape = this;
    }
}

程式的執行結果回事什麼樣子的呢?
我們來看這段程式碼

1、  System.out.println(FinalizedEscapeTestCase.caseForEscape);
2、  FinalizedEscapeTestCase.caseForEscape = newFinalizedEscapeTestCase();
3、  System.out.println(FinalizedEscapeTestCase.caseForEscape);
4、  FinalizedEscapeTestCase.caseForEscape=null;
5、  System.gc();
6、  Thread.sleep(100);
7、    System.out.println(FinalizedEscapeTestCase.caseForEscape);

1、 當程式執行第一行是,因為這個物件沒有值,結果肯定是null
2、 程式第二行給該物件賦值為新開闢的一個物件
3、 第三行列印的時候,肯定是第二行物件的hash程式碼
4、 第四行將該物件重新置為null
5、 第五行觸發GC
6、 為了保證GC能夠順利執行完畢,第六行等待100毫秒
7、 第七行列印對應的值,回事null麼?一定會是null麼?

我們來看一下對應的執行結果

相關文章