首先來看GC的幾個重點
- 不論物件的scope(local variable/member variable),所有的物件都是儲存在Heap space!
- GC是JVM用來回收記憶體的機制,符合GC條件的物件會在GC過程中,釋放其佔用的記憶體空間(Heap space)
- GC讓開發者不用處理記憶體管理,在開發時可以專注在業務邏輯上
- Java透過一個叫Garbage Collector的daemon(背景)thread來進行GC
- 開發者無法強制執行GC,GC只會在JVM認為需要(根據Heap Size)時才會執行
- System.gc()和Runtime.gc()[註1]會送GC請求給JVM,但JVM並不一定會執行GC
- 若Heap space沒空間存放新建立的物作,則JVM會丟出OutOfMemoryError或 java.lang.OutOfMemoryError heap space
- Minor GC和Major/Full都是"Stop the World"事件,只是Minor GC時間非常短(幾百milli-seconds),使用者較不容易察覺;而Full GC時間相對上長很多,且heap size愈大時間愈久;因此應儘量避免或減少Full GC發生。
物件什麼時候會符回GC條件
物件若被JVM認為沒有在使用,即符合GC條件。判斷方式則是:JVM會從根物件開始走訪所有reference,把走到的做註記,當全部走完後,沒被註記的物件代表沒有人能夠取得,即符合GC條件。一般而言,假設我們要讓物件A符合回收條件,可以通過下列方式達成:
- 將所有參照到A的變數設為null (ex. sample = null;)
- 在method中建立的物件A,一旦離開了該method,則所有local variable參照都會失效
- 將parent object reference設為null。parent object會參照到物件A,當parent object的reference消失時,則物件A也符合GC條件
- 若物件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條件,則提前結束)。
- JVM配置Eden記憶體空間給新建立的物件
- 當Minor GC時(Eden space無法分配記憶體空間給新建立的物件),物件從Eden被移到Survival space
- 當Minor GC時,物件從Survival space被移到另一個Survival space(根據設定,這可能會發生很多次)
- 當Minor GC時,物件從Survival space移(promote)至Tenured/Old generation
- Major GC(old generation無法分配空間給被promote的物件)
[註2]All literal strings and string-valued constant expressions are interned.透過String.intern(),可以讓相同值的字串只存一份在記憶體,在JDK6,intern的字串存放於Permanent generation,在JDK7之後移至Young/Old generation
No comments:
Post a Comment