1. 程式人生 > >Java JVM(二):垃圾回收概念 與 GC 日誌

Java JVM(二):垃圾回收概念 與 GC 日誌

包括:
一. 垃圾回收基本概念
二. GC日誌

一. 垃圾回收基本概念
      在JVM 中,最需要進行回收的地方就是JVM 方法區  和 JVM 堆

1.1 可達性分析演算法
        回收的時候,主要是根據可達性分析演算法。
如果一個物件不可達,那麼就是可以回收的;如果一個物件可達,那麼這個物件就不會被回收。那麼,對於可達性分析演算法,它是通過一系列稱為 "GC Roots" 的物件作為起始點,當一個物件到GC Root 沒有任何引用鏈相接的時候,則證明這個物件不可用,即可以進行回收。如下圖:

       這個GC Root 物件可以是一些靜態的物件,Java方法的local變數或引數, native 方法引用的物件,活著的執行緒。

Ps:

        有些資料認為 垃圾回收根據引用計數法,但是在本人的測試中結果是用可達性分析演算法。證明如下:

有如下程式(Student 物件1 -->Student物件2 -->Student物件3):

public class Test {
	public static void main(String[] args) throws InterruptedException {
		Student stu = new Student(new Student(new Student()));
		
		Thread.sleep(15 * 1000);
		
		stu = null;
		
		Thread.sleep(100 * 1000);
	}
}
class Student {
	private String name;
	private Student stu;
	public Student() {}
	public Student(Student stu){this.stu = stu;};
}

那麼,結果是一開始如下圖確實有3個例項,但是在stu = null 之後,例項就沒有,已經被GC回收。所以說不是程式計數法來判斷是否回收:

1.2 回收方式

        回收方式會有多種,比如說標記-清除法;複製演算法;標記-整理法。

        複製演算法:JVM 的堆記憶體會分為新生代和老年代,新生代主要就是採用複製演算法。複製演算法的主要概念是:把記憶體分為AB兩塊,每次只是使用其中一塊,當A記憶體使用完之後,就把A 中還存活的物件複製到另外一塊記憶體中去。這樣的有點就是不需要考慮記憶體碎片的問題。缺點就是記憶體減半,代價太高。但是在新生代的實際應用中,不會55分,而是按8:1:1 的比例,分為1個Eden區 和兩個Survivor 區域。這就是複製演算法。

      標記整理法:JVM的老年代就是用 標記整理法,也就是說,當需要GC 的時候,會先標記,然後把存活的物件都移到一端。

二. GC日誌

圖1

圖2

Full GC 資訊與 Minor GC 的資訊是相似的,這裡就不一個一個的畫出來了。
       從 Full GC 資訊可知,新生代可用的記憶體大小約為 18M,則新生代實際分配得到的記憶體空間約為 20M(為什麼是 20M? 請繼續看下面…)。老年代分得的記憶體大小約為 42M,堆的可用記憶體的大小約為 60M。可以計算出: 18432K ( 新生代可用空間 ) + 42112K ( 老年代空間 ) = 60544K ( 堆的可用空間 )
       新生代約佔堆大小的 1/3,老年代約佔堆大小的 2/3。也可以看出,GC 對新生代的回收比較樂觀,而對老年代以及方法區的回收並不明顯或者說不及新生代。並且在這裡 Full GC 耗時是 Minor GC 的 22.89 倍。


總結:

  1. 主要回收場所:JVM方法區,JVM方法棧。
  2. 回收方式: 新生代(複製演算法),老年代(標記整理法)。
  3. 根據Young / Old 判斷是新生代還是老年代的 GC,然後就是給出 Young / Old ,整個堆記憶體 GC 前後的大小。