JVM 學習之垃圾回收

什麼是「垃圾」?

對咱們已經沒有使用價值的東西就稱爲「垃圾」。在 JVM 中無用的對象就稱爲「垃圾」,由於它佔用了咱們寶貴的內存資源。算法

哪裏有「垃圾」?

前面咱們已經瞭解過了 JVM 的內存,如今複習一下。咱們知道程序計數器、虛擬機棧、本地方法棧都是線程私有的,這幾個區域隨着線程消亡而消亡,能夠達到自動清理的效果。Java 堆、方法區則是共享區域,各類對象的實例保存在 Java 堆中。因此這兩個區域會出現「垃圾」數組

誰是「垃圾」?

怎麼肯定誰是垃圾,這裏提供了兩種算法,分別是:引用計數算法、可達性分析算法線程

引用計數算法

引用計數算法的思路是這樣的:code

每當有一個地方引用它時,計數器的值就加 1;當引用失效的時候,計數器就減 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;
    }
}
 
class MyObject{
    public Object object = null;
}

這兩個對象已經不可能再被訪問了,可是由於他們還互相引用着對方,致使引用計數都不爲 0,垃圾回收器沒法回收他們。內存

可達性分析算法

這個算法的基本思路是:經過一系列的稱爲 「GC Roots」 的對象做爲起點,從這些節點開向下搜搜,搜索所走過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的時候就說明對象是不可用的。資源

垃圾回收算法

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

這是最基礎的垃圾回收算法,思想簡單,這個算法分爲兩個階段,一個是「標記」,一個是「清除」。字符串

算法大體思路:虛擬機

標記出全部須要回收的對象,在標記完成以後統一回收全部被標記的對象

缺點:效率低,標記和清除的效率都不高;標記清除以後會產生大量不連續的內存碎片。

複製(Copying)算法

這個算法實現簡單、高效、不容易產生碎片,可是將可用內存縮小爲原來內存的一半,來看看這個算法是什麼思路:

將可用內存按容量分爲大小相等的兩塊,每次使用其中的一塊,當這塊內存用完了,就將還存活着的對象複製到另一塊內存上面,而後把這塊已經使用的內存一次性清理掉。簡單粗暴。

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

算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存

分代收集算法

分代收集算法是目前大部分JVM的垃圾收集器採用的算法。根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠根據不一樣代的特色採起最適合的收集算法。

內存分配與回收策略

咱們知道,對象的分配就是在 Java 堆上分配,對象主要分配在新生代 Eden 區上,也可能分配在別的區域上(可設置內存相關參數)。

對象優先分配在 Eden

大多數狀況下,對象在新生代 Eden 區域中分配。若是 Eden 區域沒有足夠的空間分配的時候,就會觸發 Minor GC

大對象直接進入老年代

什麼樣的對象稱爲大對象?所謂的大對象是指須要大量連續內存空間的 Java 對象,典型的大對象就是那種很長的字符串以及數組。

長期存活的對象將進入老年代

虛擬機須要解決哪些對象放在新生代,哪些對象應該放在老年代的問題。爲了解決這個問題,虛擬機給對象定義了一個對象年齡的計數器。若是對象在 Eden 出生而且第一次 Minor GC 後仍然存活,而且能被 Survivor 容納的話,將被移動到 Survivor 空間中,而且將對象的年齡設置爲 1。若是這個對象在 Survivor 空間中可以熬過 Minor GC,年齡再增長 1 歲,當年齡增長到必定程度時(默認爲 15 歲),就會晉升到老年代。

相關文章
相關標籤/搜索