天天看點

面試阿裡,這篇JVM垃圾回收算法就夠了(下)4 GC算法5 Java中引用的種類

4 GC算法

知道了如何判定:

  • 一個對象是無效對象
  • 一個類是無用類
  • 一個常量是廢棄常量

也就知道了垃圾收集器會清除哪些資料,那麼它們是如何清除這些資料的呢?

4.1 标記-清除算法(Mark-Sweep)

最基礎的收集算法,因為後續算法也都是基于該思路,對其不足之處進行優化而得。

  • 首先判斷需要清除哪些資料,并給它們做上标記
  • 然後清除被标記的資料

缺陷

标記和清除過程效率都不高,且标記清除之後存在大量記憶體碎片,降低了空間使用率這會導緻日後因為存儲大對象時無法找到足夠連續記憶體而提前觸發GC。

面試阿裡,這篇JVM垃圾回收算法就夠了(下)4 GC算法5 Java中引用的種類

4.2 複制算法(Copying)

  • 将記憶體分成大小相等兩份,隻将資料存儲在其中一塊上.當需要回收時,也是首先标記出廢棄的資料,然後将有用的資料複制到另一塊記憶體上,最後将第一塊記憶體全部清除.
  • 面試阿裡,這篇JVM垃圾回收算法就夠了(下)4 GC算法5 Java中引用的種類
  • 分析

這種算法避免了空間碎片,但記憶體縮小了一半.

而且每次都需要将有用的資料全部複制到另一片記憶體上去,效率不高.

解決空間使用率問題

在新生代中,由于大量的對象都是"朝生夕死",也就是一次垃圾收集後隻有少量對象存活,是以我們可以将記憶體劃分成三塊:Eden、Survior1、Survior2,記憶體大小分别是8:1:1.配置設定記憶體時,隻使用Eden和一塊Survior1.當發現Eden+Survior1的記憶體即将滿時,JVM會發起一次MinorGC,清除掉廢棄的對象,并将所有存活下來的對象複制到另一塊Survior2中.接下來就使用Survior2+Eden進行記憶體配置設定.

通過這種方式,隻需要浪費10%的記憶體空間即可實作帶有壓縮功能的垃圾收集方法,避免了記憶體碎片的問題.

什麼是配置設定擔保?

當JVM準備為一個對象配置設定記憶體空間時,發現此時Eden+Survior中空閑的區域無法裝下該對象,那麼就會觸發MinorGC,對該區域的廢棄對象進行回收.但如果MinorGC過後隻有少量對象被回收,仍然無法裝下新對象,那麼此時需要将Eden+Survior中的所有對象都轉移到老年代中,然後再将新對象存入Eden區.這個過程就是"配置設定擔保".

4.3 标記-整理算法(Mark-Compact)

效率偏低。

GC前的标記過程仍與"标記-清除"一樣,但後續不是直接清理可回收對象,而是将所有存活的對象移到一端,然後直接清掉端邊界之外的記憶體。

面試阿裡,這篇JVM垃圾回收算法就夠了(下)4 GC算法5 Java中引用的種類

一種老年代GC算法,老年代中的對象一般壽命較長,是以每次GC後會有大量對象存活,是以若選用"複制"算法,則每次需要很多的複制操作,效率很低。

在新生代中使用"複制"算法,當Eden+Survior中都裝不下某對象時,可使用老年代的記憶體進行"配置設定擔保"。而若在老年代使用“複制”算法,若老年代出現Eden+Survior裝不下某個對象時,沒有其他區域給它作配置設定擔保。是以,老年代中一般使用"标記-整理"算法。

4.4 分代收集算法(Generational Collection)

目前商業虛拟機都采用此算法.根據對象存活周期的不同将Java堆劃分為老年代和新生代,根據各個年代的特點使用最佳的收集算法.

  • 老年代中對象存活率高,無額外空間對其配置設定擔保,必須使用"标記-清理"或"标記-整理"算法;
  • 新生代中存放"朝生夕死"的對象那就用複制算法,隻需要付出少量存活對象的複制成本就可以完成收集.

5 Java中引用的種類

Java中根據生命周期的長短,将引用分為4類

強引用

我們平時所使用的引用就是強引用.

類似A a = new A();

也就是通過關鍵字new建立的對象所關聯的引用就是強引用.

隻要強引用還存在,該對象永遠不會被回收.

軟引用

一些還有用但并非必需的對象

隻有當堆即将發生OOM異常時,JVM才會回收軟引用所指向的對象.

軟引用通過SoftReference類實作.

軟引用的生命周期比強引用短一些.

弱引用

也是描述非必需對象,比軟引用更弱

所關聯的對象隻能存活到下一次GC發生前.

隻要垃圾收集器工作,無論記憶體是否足夠,弱引用所關聯的對象都會被回收.

弱引用通過WeakReference類實作.

虛引用

也叫幽靈(幻影)引用,最弱的引用關系.

它和沒有引用沒有差別,無法通過虛引用取得對象執行個體.

設定虛引用唯一的作用就是在該對象被回收之前收到一條系統通知.

虛引用通過PhantomReference類來實作.