天天看點

JVM系列(二)--垃圾收集

如果從垃圾收集進行分析,我覺得可以從兩個方面進行:

1.如何判斷某一個對象可以進行回收

2.在哪些Runtime Data Area進行回收

3.如何進行回收

一.判斷某個對象是否可以回收

主流的商用語言,如Java及C#甚至Lisp都是采用GC Roots Tracing(根搜尋算法)判斷某個對象是否可以進行回收。具體的做法是標明一些可以作為GC Roots的對象,如:

a.JVM Stack的Stack Frame中的局部變量表中引用的對象。

b.Method Area中類的靜态屬性引用的對象。

c.Method Area中的常量引用的對象。

d.Native Method Area中引用的對象。

如果某一個對象到GC Roots沒有Reference Chain,則該對象被判定為可以回收。

大緻的回收過程如下:

1.以根搜尋算法判定某個對象到GC Roots是否存在Reference Chain,進行第一次标記并判斷是否有必要執行finalize()方法。

2.如被判定有必要執行finalize()方法,則進入F-Queue隊列中,進行第二次标記,如果在finalize()執行過程中未建立到GC Roots的Reference Chain,則該對象注定要被回收,反之則被移除即将會後的集合。

二.在哪些區域進行回收

分析這個問題需要從配置設定的角度去了解:

前提,Java Heap 可以分為新生代與老年代

1.大多數情況下,對象在新生代Eden區中配置設定。

2.大對象直接進入老年代。

3.長期存活的對象進入老年代。

根據以上配置設定政策,可以知道,回收主要發生在Java Heap中。

是否要對Method Area進行回收?

首先我們知道,HotSpot的Method Area存在垃圾回收。

根據以往的資料分析結果,Method Area發生的垃圾回收效率太低,主要因為以下原因:

Method Area中垃圾回收主要針對廢棄的常量和無用的類,回收廢棄的常量較為容易,但是回收無用的類卻較為麻煩,因為一個類是否需要回收的判定條件太苛刻:

1.該類所有的執行個體都已經被回收,即Java Heap中不存在該類的任何執行個體

2.加載該類的ClassLoader已經被回收

3.該類對應的java.lang.Class對象沒有在任何地方被引用,無法在熱河地方通過反射通路該類的方法

當然,可以通過一下幾個參數進行控制:

-Xnoclassgc 不回收類

三.如何進行回收

先來看幾種垃圾回收算法的原理及優缺點:

1.Mark-Sweep(标記清除)算法

先标記,後清除。

缺點:容易産生大量的不連續記憶體(記憶體碎片),這樣在配置設定大對象時較為麻煩。

2.Copying(複制算法)

複制算法的思想:

将記憶體非為兩塊,每次隻使用一塊,當這一塊用完了,就将這塊記憶體中存活的對象複制到另外一塊記憶體上,然後将第一塊記憶體中的對象進行一次性回收。

優點:高效

缺點:将記憶體一份為二,代價較高。

現在的商業虛拟機都使用這種算法對新生代進行回收,如HotSpot對新生代進行回收的方式:

将新生代分為Eden空間、From Survivor空間、To Survivor空間,預設比例為8:1:1

每次回收時将Eden空間與其中一塊Survivor空間中存活的對象複制到另外一塊Survivor空間上,然後進行清除,這其中還涉及到配置設定擔保的概念

3.Mark-Compact(标記整理)算法

針對老年代的回收算法

每次回收時進行整理,将未被标記的對象(存活對象)移動到一端,然後清理掉邊界以外的記憶體。

4.Generational Collection(分代收集)

分代垃圾回收的思想是針對不同區域的特點,在不同區域應用不同的回收算法。

新生代:Mark-Copying-Sweep

老年代: Mark-Compact-Sweep

Minor GC與Full GC