Java垃圾回收機制詳談

垃圾回收是在內存中存在沒有引用的對象或者超過作用域的對象時進行的。
垃圾回收的目的是識別並且丟棄應用不再使用的對象來釋放和重用資源。

垃圾回收機制中的算法
Java語言規範沒有明確的說明JVM使用哪一種垃圾回收算法,但是任何一種垃圾回收算法一般要做兩件基本的事情:(1)、發現無用信息對象。(2)、回收無用對象佔用的內存空間,是該控件可被程序再次使用。

1、引用計數法
引用計數法是垃圾收集器中的早期策略。在這種方法中,堆中每個對象實例都有一個引用計數。當一個對象被創建時,且將該對象實例分配給一個變量,該變量計數設置爲1。當任何其他變量被賦值爲這個對象的引用時,計數加1(a=b,則b引用的對象實例的計數器加1),但當一個對象實例的某個引用超過了生命週期或者被設置爲一個新值時,對象實例的引用計數器減一。任何引用計數器爲0的對象實例可以被當作垃圾收集。當一個對象實例被垃圾收集時,他引用的任何對象實例的引用計數器減1。

優點:
引用計數器可以很快地執行,交織在程序運行中。對程序需要不被長時間打斷的實時環境比較有利。

缺點:
無法檢測出循環引用。如父對象有一個子對象的引用,子對象反過來引用父對象,這樣,他們的引用計數器永遠不可能爲0。

public class Main{
	public static void main(String[] args){
		MyObject object1=new MyObject();
		MyObject object2=new MyObject();
		
		object1.object=object2;
		object2.object=object1;

		object1=null;
		object2=null;
	}
}

object1和object2指向的對象已經不可能再被訪問,但是由於他們互相引用對方,導致他們的引用計數器都不爲0,那麼垃圾收集器就永遠不會回收它們。

2、tracing算法(標記-清除算法)
在這裏插入圖片描述
根搜索算法是從離散數學中的圖論引入的,程序把所有的引用關係看作一張圖,從節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個點的引用節點,當所有的引用節點尋找完畢後,剩餘的節點則被認爲是沒有引用到的節點,即無用節點。

在java中可以作爲GC ROOT的對象有:

  • 虛擬機棧中引用的對象(本地變量表)
  • 方法區中靜態屬性引用的對象
  • 方法區中常量引用的變量
  • 本地方法棧中引用的對象(Native對象)

示意圖:
在這裏插入圖片描述
標記-清除算法採用從根集合進行掃描,對存活對象標記,標記完畢後,再掃描整個空間中未被標記的對象進行回收。
標記-清除算法不需要進行對象的移動,並且僅對不存活的對象進行處理,在存活對象比較多的情況下極爲高效,但由於標記-清除算法直接回收不存活的對象,因此會造成內存碎片化。

3、compacting算法(標記-整理算法)
在這裏插入圖片描述
標記-整理算法在清除回收不存活對象佔用的空間後,會將所有的存活對象王座端空閒空間移動,並更新對應的指針。因此成本更多,但是卻解決了內存碎片的問題。

4、copying算法

該算法的提出是爲了克服句柄的開銷和解決碎片化的垃圾回收。它開始時把堆分爲一個對象面和多個空閒面,程序從對象面爲對象分配空間,當對象滿了,基於copying算法的垃圾收集就從根集中掃描活動對象,並將每個活動對象複製到下面的空閒面,這樣空閒面變成了對象面,原來的對象面變成了空閒面,程序會在新的對象中分配內存。

5、generation算法
分代的垃圾回收策略,是基於這樣一個事實:不同的對象的生命週期是不一樣的,因此不同生命週期的對象可以採用不同的回收算法,以便提高回收效率。

年輕代:

  1. 所有新生成的對象首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象。
  2. 新生代內存按照8:1:1的比例分爲一個eden對象和兩個survivor(survivor0,survivor1)區。大大部分對象在Eden區中生成。回收時先將eden區存活的對象複製到一個survivior0區,然後清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活對象複製到另一個survivor1區,然後清空eden和整個survivor0區,此時survivor0區是空的,然後將survivor0區和survivor1區交換,即保持survivor1區爲空,如此往復。
  3. 當survivor1區不足以存放eden和survivor0的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發一個Full GC,也就是新生代、老年代都進行回收。
  4. 新生代發生的GC也叫Minor GC,Minor GC發生頻率比較高。

年老代:

  1. 在年輕代中經歷了N次垃圾回收後仍然存活對象,就會被放到年老代中,因此,可以認爲老年代中存放的都是一些生命週期較長的對象。
  2. 內存比新生代也大很多,大概比例爲1:2,當老年代內存滿時觸發Major GC,即Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高。

持久化:
用於存放靜態文件,如Java類、方法等。持久化對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等,在這種時候需要設置一個比較大的持久化空間來存放這些運行過程中新增的類。

GC(垃圾收集器)
新生代收集器使用的收集器: Serial(複製算法),PraNew(停止-複製算法),Parallel Scavenge(停止-複製算法)
老年代收集器使用的收集器:Serial Old(標記-清除算法),Parallel Old(停止-複製算法),CMS(標記-清理算法)

GC的執行機制

Scavenge GC
一般情況下,當新對象生成,並且在Eden申請空間失敗時,就會觸發Scavenge GC,對Eden區域進行GC,清除非存活對象,並且把尚且存活的對象移動到survivor區,然後整理survivor的兩個區。這種方式的GC是對年輕代的Eden進行,不會影響到年老代。因爲大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,所以Eden區的GC會頻繁發生。

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