1. 程式人生 > >Java記憶體管理之用什麼方式回收垃圾、管理記憶體呢?垃圾回收演算法有哪些?

Java記憶體管理之用什麼方式回收垃圾、管理記憶體呢?垃圾回收演算法有哪些?

文章目錄


在這裡插入圖片描述


1. 什麼是垃圾?

所有 GC Roots 不可達的物件都稱為垃圾,參考下圖,黑色的表示垃圾,灰色表示存活物件,綠色表示空白空間。
在這裡插入圖片描述


2. 標記-清理(Marked-Sweep)

所謂“標記”就是利用可達性遍歷堆記憶體,把“存活”物件和“垃圾”物件進行標記,得到的結果如上圖;
第二步,既然“垃圾”已經標記好了,那我們再遍歷一遍,把所有“垃圾”物件所佔的空間直接 清空 即可。標記-清理 方案,簡單方便 ,但是容易產生 記憶體碎片。
在這裡插入圖片描述


3. 標記-整理(Marked-Compact)

既然上面的方法會產生記憶體碎片,那好,我在清理的時候,把所有 存活 物件扎堆到同一個地方,讓它們待在一起,這樣就沒有記憶體碎片了。這兩種方案適合 存活物件多,垃圾少 的情況,它只需要清理掉少量的垃圾,然後挪動下存活物件就可以了。
在這裡插入圖片描述


4. 複製(Copying)

這種方法比較粗暴,直接把堆記憶體分成兩部分,一段時間內只允許在其中一塊記憶體上進行分配,當這塊記憶體被分配完後,則執行垃圾回收,把所有 存活 物件全部複製到另一塊記憶體上,當前記憶體則直接全部清空。
在這裡插入圖片描述


6. Java分代回收機制

Java 裡是如何選擇利用這三種回收演算法呢?
常規的 Java 堆至少包括了 新生代 和 老年代 兩塊記憶體區域,而且這兩塊區域有很明顯的特徵:

  • 新生代:存活物件少、垃圾多
  • 老年代:存活物件多、垃圾少

結合新生代/老年代的存活物件特點和之前提過的幾種垃圾回收演算法,可以得到如下的回收方案:

6.1 新生代-複製 回收機制(Minor GC,把記憶體按 8:1:1 分)

在這裡插入圖片描述
依次取名為 Eden、Survivor A、Survivor B 區,其中 Eden 意為伊甸園,形容有很多新生物件在裡面建立;Survivor區則為倖存者,即經歷 GC 後仍然存活下來的物件。

工作原理

  1. 首先,Eden區最大,對外提供堆記憶體。當 Eden 區快要滿了,則進行 Minor GC,把存活物件放入 Survivor A 區,清空 Eden 區;
  2. Eden區被清空後,繼續對外提供堆記憶體;
  3. 當 Eden 區再次被填滿,此時對 Eden 區和 Survivor A 區同時進行 Minor GC,把存活物件放入 Survivor B 區,同時清空 Eden 區和Survivor A 區;
  4. Eden區繼續對外提供堆記憶體,並重覆上述過程,即在 Eden 區填滿後,把 Eden 區和某個 Survivor 區的存活物件放到另一個 Survivor 區;
  5. 當某個 Survivor 區被填滿,且仍有物件未被複制完畢時,或者某些物件在反覆 Survive 15 次左右時,則把這部分剩餘物件放到Old 區;
  6. 當 Old 區也被填滿時,進行 Major GC,對 Old 區進行垃圾回收。


6.2 老年代-標記整理 回收機制(Full GC)

老年代一般存放的是存活時間較久的物件,所以每一次 GC 時,存活物件比較較大,也就是說每次只有少部分物件被回收。因此,根據不同回收機制的特點,這裡選擇 存活物件多,垃圾少 的標記整理 回收機制,僅僅通過少量地移動物件就能清理垃圾,而且不存在記憶體碎片化。


6.3 總結

Minor GC 是新生代Copying演算法。MinorGC觸發條件:
(1)當Eden區滿時,觸發Minor GC。

Full GC 的老年代,採取的Marked-Compact。Full GC觸發條件:
(1)呼叫System.gc時,系統建議執行Full GC,但是不必然執行。
(2)老年代空間不足。
(3)方法區空間不足。
(4)通過Minor GC後進入老年代的平均大小大於老年代的可用記憶體。

總結:新生代 採用 回收 機制,老年代 採用 標記整理 機制