高級程序員知識學習(GC垃圾回收機制算法相關知識)

引用計數法(在對象的相互引用的時候出現永遠不可能進行垃圾的回收)

在 Java 中,引用和對象是有關聯的。如果要操作對象則必須用引用進行。因此,很顯然一個簡單的辦法是通過引用計數來判斷一個對象是否可以回收。簡單說,即一個對象如果沒有任何與之關聯的引用, 即他們的引用計數都不爲 0, 則說明對象不太可能再被用到,那麼這個對象就是可回收對象。

可達性分析

爲了解決引用計數法的循環引用問題, Java 使用了可達性分析的方法。通過一系列的“GC roots”對象作爲起點搜索。如果在“GC roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的。要注意的是,不可達對象不等價於可回收對象, 不可達對象變爲可回收對象至少要經過兩次標記過程。兩次標記後仍然是可回收對象,則將面臨回收。

標記清除算法:

最基礎的垃圾回收算法,分爲兩個階段,標註和清除。標記階段標記出所有需要回收的對象,清除階段回收被標記的對象所佔用的空間。如圖:

從圖中我們就可以發現,該算法最大的問題是內存碎片化嚴重,後續可能發生大對象不能找到可利用空間的問題。

複製算法:

爲了解決 Mark-Sweep 算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分爲等大小的兩塊。每次只使用其中一塊,當這一塊內存滿後將尚存活的對象複製到另一塊上去,把已使用的內存清掉,如圖:

這種算法雖然實現簡單,內存效率高,不易產生碎片,但是最大的問題是可用內存被壓縮到了原本的一半。且存活對象增多的話, Copying 算法的效率會大大降低

標記整理算法(Mark-Compact)

結合了以上兩個算法,爲了避免缺陷而提出。標記階段和 Mark-Sweep 算法相同, 標記後不是清理對象,而是將存活對象移向內存的一端。然後清除端邊界外的對象。如圖:

分代收集算法

分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不同生命週期將內存劃分爲不同的域,一般情況下將 GC 堆劃分爲老生代(Tenured/Old Generation)和新生代(YoungGeneration)。老生代的特點是每次垃圾回收時只有少量對象需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的算法

新生代與複製算法

目前大部分 JVM GC 對於新生代都採取 Copying 算法,因爲新生代中每次垃圾回收都要回收大部分對象,即要複製的操作比較少,但通常並不是按照 1 1 來劃分新生代。一般將新生代劃分爲一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另一塊 Survivor 空間中。

https://img-blog.csdn.net/20141107224401036?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhiMTIzR0dHR0dH/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

老年代與標記複製算法

老年代因爲每次只回收少量對象,因而採用 Mark-Compact 算法

  1. JAVA 虛擬機提到過的處於方法區的永生代(Permanet Generation)它用來存儲 class 類,

常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

  1. 對象的內存分配主要在新生代Eden Space和 Survivor Space 的 From Space(Survivor 目前存放對象的那一塊),少數情況會直接分配到老生代。
  2. 當新生代的EdenSpace和 From Space 空間不足時就會發生一次 GC,進行 GC 後,Eden

Space 和 From Space 區的存活對象會被挪到 To Space,然後將 Eden Space 和 From Space 進行清理。

  1. 如果 To Space 無法足夠存儲某個對象,則將這個對象存儲到老生代。
  2. 在進行 GC 後,使用的便是 Eden Space 和 To Space 了,如此反覆循環。
  3. 當對象在 Survivor 區躲過一次 GC 後,其年齡就會+1。 默認情況下年齡到達 15 的對象會被移到老生代中

 

JAVA 四中引用類型

強引用

在 Java 中最常見的就是強引用, 把一個對象賦給一個引用變量,這個引用變量就是一個強引用。當一個對象被強引用變量引用時,它處於可達狀態,它是不可能被垃圾回收機制回收的,即使該對象以後永遠都不會被用到 JVM 也不會回收。因此強引用是造成 Java 內存泄漏的主要原因之

軟引用

軟引用需要用 SoftReference 類來實現,對於只有軟引用的對象來說,當系統內存足夠時它不會被回收,當系統內存空間不足時它會被回收。軟引用通常用在對內存敏感的程序中。

弱引用

弱引用需要用 WeakReference 類來實現,它比軟引用的生存期更短,對於只有弱引用的對象來說,只要垃圾回收機制一運行,不管 JVM 的內存空間是否足夠總會回收該對象佔用的內存。虛引用

虛引用需要 PhantomReference 類來實現,它不能單獨使用,必須和引用隊列聯合使用。 虛引用的主要作用是跟蹤對象被垃圾回收的狀態

分區收集算法:分區算法則將整個堆空間劃分爲連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區間,根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減少一次 GC 所產生的停頓。(G1垃圾收集器

GC 垃圾收集器

Java 堆內存被劃分爲新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收算法;年老代主要使用標記-整理垃圾回收算法,因此 java 虛擬中針對新生代和年老代分別提供了多種不同的垃圾收集器, JDK1.6 Sun HotSpot 虛擬機的垃圾收集器如下:

CMS 收集器(多線程標記清除算法)

 

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間,和其他年老代使用標記-整理算法不同,它使用多線程的標記-清除算法。最短的垃圾收集停頓時間可以爲交互比較高的程序提高用戶體驗。CMS 工作機制相比其他的垃圾收集器來說更復雜,整個過程分爲以下 4 個階段:

初始標記只是標記一下 GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。

併發標記進行 GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。

重新標記爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。

併發清除:清除 GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。由於耗時最長的併發標記和併發清除過程中,垃圾收集線程可以和用戶現在一起併發工作, 所以總體上來看CMS 收集器的內存回收和用戶線程是一起併發地執行:

G1 收集器

Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器, G1 收集器兩個最突出的改進是:

1. 基於標記-整理算法,不產生內存碎片。

2. 可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。

G1 收集器避免全區域垃圾收集,它把堆內存劃分爲大小固定的幾個獨立區域,並且跟蹤這些區域的垃圾收集進度,同時在後臺維護一個優先級列表,每次根據所允許的收集時間, 優先回收垃圾最多的區域。區域劃分和優先級區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收集效率

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