哦?原來這就是 JVM 垃圾!

小新是也發表於2021-09-11

大家都知道,JVM 有垃圾回收的機制,垃圾回收的前提是要知道:什麼是垃圾!然後再是如何識別垃圾

什麼是垃圾

垃圾,本質上就是沒有引用的物件(們),下面來介紹兩種垃圾

1. 沒有引用指向的物件

下圖是物件間引用的狀態,從正常引用到引用斷開,這個 A 和 C 的引用斷開之後,C 就成了那個垃圾。
正常到斷開連線

2. 沒有引用指向的一組物件

一個典型的案例如下圖,就是迴圈引用,這幾個物件看起來都有引用指向,但是其實他們只是一堆緊緊相擁的垃圾。
一堆垃圾

如何識別垃圾

上面介紹了什麼是垃圾,那要如何才能識別出垃圾呢?主要有兩種演算法:

  1. 引用計數法
  2. 可達性分析法

1. 引用計數法

演算法很簡單,就是在物件頭上加上被引用的次數,物件的被引用的次數為 0 之日,就是其成為垃圾之時!這個演算法的優點是垃圾回收及時,只要物件被引用次數為 0,就可以回收了。

下圖是引用計數的示意圖,物件 A、B、C 都被引用了一次
正常引用計數

如果 A 跟 C 的引用斷開,則 C 的引用次數減一,變為 0,此時 C 就是垃圾
引用計數斷開引用

引用計數法有個致命的缺點:那就是無法識別出迴圈引用!
下圖是一個迴圈引用,明明他們就是一堆垃圾,但是因為被引用次數都不為 0,引用計數法無法識別出他們是垃圾。
引用計數迴圈引用

2. 可達性分析法

引用計數法的缺點過於致命,目前 JVM 採用的是另一種演算法來識別垃圾:可達性分析法。

這個演算法的基本思路就是:從一系列根物件(GC Roots)開始,根據引用關係向下搜尋,如果某個物件到 GC Roots 間沒有任何引用,則證明此物件是不可能再被使用的,也就是垃圾。

其示意圖如下,左邊綠色部分的物件,都可以連向 GC Roots,所以他們都是存活的物件。而右邊灰色的部分,即使他們是迴圈引用,他們也跟 GC Roots 之間沒有連線路徑,所以灰色部分的物件是垃圾。
可達性分析法
那麼,究竟是哪些物件能成為至高無上的 GC Roots 呢?以下是主要的 GC Roots:

  • 虛擬機器中引用的物件,如各個執行緒呼叫的方法堆疊中的引數、區域性變數等。
  • 方法區中類的靜態屬性引用的物件,如類的引用型別的靜態變數。
  • 方法區中常量引用的物件,如字串常量池裡的引用。
  • 本地方法棧中 JNI(Native 方法)引用的物件。
  • 虛擬機器內部的引用,如基本資料型別對應的 Class 物件,一些常駐的異常物件(比如 NullPointExcepitonOutOfMemoryError)等,還有系統類載入器。

優點:解決引用計數器所不能解決的迴圈引用問題。
缺點:

  1. 耗時:因為需要從 GC Roots 開始逐個檢查引用;
  2. STW:GC 過程中需要保證物件的引用關係不能發生變化,所以 GC 進行時必須停頓所有執行執行緒(STW:Stop The World)。

總結

第一部分我們介紹了什麼是垃圾:沒有任何引用指向的一個或多個物件。
第二部分介紹瞭如何識別垃圾,有兩種演算法:

  1. 引用計數法:通過給物件新增被引用的次數來識別。優點是回收簡單及時;缺點是無法解決迴圈引用。
  2. 可達性分析法:從一系列根物件(GC Roots)開始,根據引用關係向下搜尋,如果某個物件到 GC Roots 間沒有任何引用,則此物件就是垃圾。優點是解決了迴圈引用;缺點是耗時和 STW。

相關文章