Java的垃圾回收機制

垃圾收集GC(Garbage Collection)是Java語言的核心技術之一, 在Java中,程序員不須要去關心內存動態分配和垃圾回收的問題,這一切都交給了JVM來處理。針對GC咱們這篇文章提出如下幾個問題,GC中斷定爲垃圾的標準,標記垃圾的算法以及回收垃圾的算法。java

什麼樣的對象纔是垃圾?

這個問題其實很簡單,對於Java對象來說,若是說這個對象沒有被其餘對象所引用該對象就是無用的,此對象就被稱爲垃圾,其佔用的內存也就要被銷燬。那麼天然而然的就引出了咱們的第二個問題,判斷對象爲垃圾的算法都有哪些?程序員

標記垃圾的算法

Java中標記垃圾的算法主要有兩種, 引用計數法和可達性分析算法。咱們首先來介紹引用計數法。算法

引用計數法

引用計數法就是給對象中添加一個引用計數器,每當有一個地方引用它,計數器就加 1;當引用失效,計數器就減 1;任什麼時候候計數器爲 0 的對象就是不可能再被使用的,能夠當作垃圾收集。這種方法實現起來很簡單並且優缺點都很明顯。bash

  • 優勢 執行效率高,程序執行受影響較小spa

  • 缺點 沒法檢測出循環引用的狀況,致使內存泄露線程

優勢咱們很好理解,那麼什麼是循環引用呢?咱們舉一個簡單的例子。3d

public class MyObject {
    public MyObject childNode;
}
複製代碼
public class ReferenceCounterProblem {
    public static void main(String[] args) {
        MyObject object1 = new MyObject();
        MyObject object2 = new MyObject();
        object1.childNode = object2;
        object2.childNode = object1;
    }
}
複製代碼

從上述代碼中咱們能夠看出,object1和object2並無任何價值,可是他們循環引用,形成內存泄露。code

可達性分析算法

這個算法的基本思想就是經過一系列的稱爲 「GC Roots」 的對象做爲起點,從這些節點開始向下搜索,節點所走過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的話,則證實此對象是不可用的。cdn

那麼什麼對象能夠做爲GCRoot?對象

  • 虛擬機棧中的引用對象

  • 方法區中的常量引用對象

  • 方法區中的類靜態屬性引用對象

  • 本地方法棧中的引用對象

  • 活躍線程中的引用對象

可達性分析算法以下圖所示

那麼不可達的對象是不是必死之局呢?答案也是否認的

在可達性分析法中不可達的對象,它們暫時處於「緩刑階段」,要真正宣告一個對象死亡,至少要經歷兩次標記過程;可達性分析法中不可達的對象被第一次標記而且進行一次篩選,篩選的條件是此對象是否有必要執行 finalize 方法。當對象沒有覆蓋 finalize 方法,或 finalize 方法已經被虛擬機調用過期,虛擬機將這兩種狀況視爲沒有必要執行。被斷定爲須要執行的對象將會被放在一個隊列中進行第二次標記,除非這個對象與引用鏈上的任何一個對象創建關聯,不然就會被真的回收。

如何將垃圾回收?

在Java中存在着四種垃圾回收算法,標記清除算法、複製算法、標記整理算法以及分代回收算法。咱們接下來會分別介紹他們。

標記清除算法

該算法分爲「標記」和「清除」兩個階段:標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。它是最基礎的收集算法,效率也很高,可是會帶來兩個明顯的問題:

  • 效率問題

  • 空間問題(標記清除後會產生大量不連續的碎片)

該算法具體流程以下圖所示

複製算法

爲了解決效率問題,咱們開發出了複製算法。它能夠將內存分爲大小相同的兩塊,每次使用其中的一塊。當第一塊的內存使用完後,就將還存活的對象複製到另外一塊去,而後再把使用的空間一次清理掉。這樣就使每次的內存回收都是對內存區間的一半進行回收。

簡單來講就是該對象分爲對象面以及空閒面,對象在對象面上建立,對象面上存活的對象會被複制到空閒面,接下來就能夠清除對象面的內存。

這種算法的優缺點也比較明顯

  • 優勢:解決碎片化問題,順序分配內存簡單高效

  • 缺點:只適用於存活率低的場景,若是極端狀況下若是對象面上的對象所有存活,就要浪費一半的存儲空間。

標記整理算法

爲了解決複製算法的缺陷,充分利用內存空間,提出了標記整理算法。該算法標記階段和標記清除同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存。 以下圖所示:

分代收集算法

當前虛擬機的垃圾收集都採用分代收集算法,這種算法就是根據具體的狀況選擇具體的垃圾回收算法。通常將 java 堆分爲新生代和老年代,這樣咱們就能夠根據各個年代的特色選擇合適的垃圾收集算法。

好比在新生代中,每次收集都會有大量對象死去,因此能夠選擇複製算法,只須要付出少許對象的複製成本就能夠完成每次垃圾收集。而老年代的對象存活概率是比較高的,並且沒有額外的空間對它進行分配擔保,因此咱們必須選擇「標記-清除」或「標記-整理」算法進行垃圾收集。

本篇文章咱們介紹了 JavaGC 方面的內容,主要講了判斷垃圾的機制,以及經常使用的一些垃圾回收算法,但願可以對你有所幫助。

相關文章
相關標籤/搜索