深入理解JAVA虛擬機(二)垃圾收集與內存分配策略

一.JAVA虛擬機垃圾回收機制

1.判斷一個對象是否存活

1.1 引用計數算法:給對象中添加一個引用計數器,每當有引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器爲0的對象就是不可能再被使用。

1.2 可達性分析算法:在Java中,都是通過可達性分析來對象是否存活的(如果對象是死的,那麼它所佔用的內存就是需要回收的)。可達性分析算法的基本思想就是通過一系列被稱爲“GC Roots”的對象開始,從這些節點向下搜索,搜索走過的路線稱爲引用鏈。當一個對象沒有在任何引用鏈上出現,則這個對象會被判定爲不可用的(死的,可回收的)。

       在被可達性分析算法判定爲不可用的對象,也並非是一定就是會被回收的,它們還會經歷一次篩選的過程,篩選的條件就是此對象是不是要執行finalize()方法,如果對象沒有覆蓋finalize()方法或它的finalize()方法在上一次垃圾回收器工作時已經執行過了,則被判定爲不用執行finalize()方法(對象會在這次回收中被回收),若判定爲需要執行finalize()方法,則這個對象會被放置在一個F-Queue隊列中,稍後虛擬機會建立一個finalizer線程(低優先級)來觸發這個方法,但虛擬機不承諾會等它執行完這個方法。(也就是這個對象可能在執行finalize()方法時被回收了),如果在finalize()方法中,對象加入了任何一個引用鏈中,則這個對象在這次回收器工作時就不會被回收了。

       在Java中,有幾種可作爲GC Roots對象:虛擬機棧(棧幀中的本地變量表)中引用對象。方法區中類靜態屬性和常量引用的對象和本地方法棧中JNI引用的對象;

二.垃圾回收算法

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

首先會利用前面的可達性分析算法標記出需要回收的對象,在標記完成後就統一回收所有被標記的對象,這個算法的缺點主要有:
•效率問題,在標記和清除兩個過程中效率都不高;
•空間問題,標記清除之後會產生大量的內存碎片,碎片太多,可能導致在下次爲大對象分配內存時,提前觸發一次垃圾回收動作;

2.複製算法(Coping)

將可用的內存分爲兩塊,每次只使用其中的一塊,這樣每次只需要順序分配內存就可以,當一塊的內存用完後,就把還存活的對象複製到另一塊內存中去,然後對使用過的內存空間進行回收就可以了。(一般不會採用平均分成兩塊的方式,現代虛擬機一般會將內存分成一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和一塊Survivor空間,回收時,將Eden空間和Survivors空間裏還存活的對象複製到另一塊沒有使用的Survivor空間中,然後清理掉用過的空間),一般會這種算法回收新生代的內存空間。

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

先利用可達性分析算法標記需要回收的對象,然後就讓還存活的對象(出現在任何引用鏈中的對象)都向一端移動,然後清理掉端邊界外的內存。(一般用來回收老年代的對象)。

三.垃圾回收的時間

大多數情況下,對象優先在Eden區中分配(大對象直接在老年代分配),當Eden沒有足夠空間時,JVM就會發起一次Minor GC。在進行Minor GC 之前,JVM會檢查老年代最大可用的空間是否大於新生代所有對象的空間,如果成立,則Minor GC是安全的,否則,JVM就會去檢查HandlePromotionFailure設置值是否允許擔保失敗。如果允許擔保失敗,則會繼續檢查老年代最大可用空間是否大於歷次晉升到老年代對象的平均大小,如果是,則會嘗試進行Minor GC(若失敗,就會進行一次Full GC);否則就會改爲進行一次Full GC。

各類垃圾收集器:


垃圾收集器:
    新生代:Serial收集器,ParNew收集器,Parallel Scavenge收集器
    老年代:CMS, Seral Old(MSC), Parallel Old收集器
    GI收集器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章