12.01.2014

什麼是Garbage Collection以及他在Java如何運作

JAVA並不像C或C++必需由開發者負責記憶體分配/回收的任務,JVM會依據設定,找出沒被參照的物件,自動進行記憶體空間回收,這使得讓開發者在開發時可以專注在業務邏輯上。雖然開發者可以不用管記憶體的分配/回收,但為了系統效能最佳化,我們還是有必要了解Garbage Collection(以下簡稱GC)是如何運作的,在開始之前,我們必須強調GC雖然可以自動回收沒被參照的物件,但無法解決程式設計不當所造成的memory leak。

首先來看GC的幾個重點

  1. 不論物件的scope(local variable/member variable),所有的物件都是儲存在Heap space!
  2. GC是JVM用來回收記憶體的機制,符合GC條件的物件會在GC過程中,釋放其佔用的記憶體空間(Heap space)
  3. GC讓開發者不用處理記憶體管理,在開發時可以專注在業務邏輯上
  4. Java透過一個叫Garbage Collector的daemon(背景)thread來進行GC
  5. 開發者無法強制執行GC,GC只會在JVM認為需要(根據Heap Size)時才會執行
  6. System.gc()和Runtime.gc()[註1]會送GC請求給JVM,但JVM並不一定會執行GC
  7. Heap space沒空間存放新建立的物作,則JVM會丟出OutOfMemoryError java.lang.OutOfMemoryError heap space
  8. Minor GC和Major/Full都是"Stop the World"事件,只是Minor GC時間非常短(幾百milli-seconds),使用者較不容易察覺;而Full GC時間相對上長很多,且heap size愈大時間愈久;因此應儘量避免或減少Full GC發生。


物件什麼時候會符回GC條件

物件若被JVM認為沒有在使用,即符合GC條件。判斷方式則是:JVM會從根物件開始走訪所有reference,把走到的做註記,當全部走完後,沒被註記的物件代表沒有人能夠取得,即符合GC條件。
一般而言,假設我們要讓物件A符合回收條件,可以通過下列方式達成:
  1. 將所有參照到A的變數設為null (ex. sample = null;)
  2. 在method中建立的物件A,一旦離開了該method,則所有local variable參照都會失效
  3. 將parent object reference設為null。parent object會參照到物件A,當parent object的reference消失時,則物件A也符合GC條件
  4. 若物件A只有被WeakHashMap參照到,則符合條件

Heap Generations

Heap可分為3個generations,分別稱為: Young/New generation, Tenured/Old generation和Permanent(Perm) generation,其中Young generation可以進一步分為3個區塊: Eden space, Survivor 1和Survivor 2。當物件一開始被產生時,他被放置在Eden space,當Eden space滿了,無法存放新物件時,JVM會啟動Minor GC,把存活的物件往Survivor 1或2移及以原本在Survivor 1或2的存活物件往另一個Survivor空間移,當JVM執行多次Minor GC後,會把符合條件的存活物件往Tenured generation移,這個過程我們稱為Minor GC。Tenured generation滿了時,JVM會執行GC,回收Tenured generation的空間,我們稱之為Major/Full GC
http://javahash.com/java-memory-model-structures/

Permanent generation儲存class和method相關的metadata以及interned String[註2]。至於perm space會不會進行GC,則是依JVM而定,Sun/Oracle實作的JVM會進行GC,要確認你用的JVM是否會進行GC,可以寫支小程式建立數百萬個字串看看是否會出現GC log或 OutofMemoryError。

物件的旅程

在了解Heap Generation後,我們來看看一個物件生命週期中,他在這些generation間的旅程(並不一定會走完全程,若期間符合GC條件,則提前結束)。
  1. JVM配置Eden記憶體空間給新建立的物件
  2. 當Minor GC時(Eden space無法分配記憶體空間給新建立的物件),物件從Eden被移到Survival space
  3. 當Minor GC時,物件從Survival space被移到另一個Survival space(根據設定,這可能會發生很多次)
  4. 當Minor GC時,物件從Survival space移(promote)至Tenured/Old generation
  5. Major GC(old generation無法分配空間給被promote的物件)

[註1] System.gc()和Runtime.gc()其實做一樣的事,System.gc()轉發request給Runtime.gc(),差別是System.gc()是class level而Runtime.gc()是instance level,System.gc()較為方便而已
[註2]All literal strings and string-valued constant expressions are interned.透過String.intern(),可以讓相同值的字串只存一份在記憶體,在JDK6,intern的字串存放於Permanent generation,在JDK7之後移至Young/Old generation
你可能對下面主題有興趣:
  1. 關於GC的JVM參數

No comments: