Java 新生代與老年代
Java 中的記憶體區域主要有堆和棧兩部分。由於棧是執行緒私有,隨著執行緒的結束而結束,因此垃圾回收主要在堆中進行,堆中幾乎存放了 Java 中所有的物件例項。
堆記憶體模型
堆記憶體分為兩大部分:新生代和老年代,比例關係為1:2。 新生代又分為 Eden、ServivorFrom、ServivorTo 三個區域,比例為8:1:1。ServivorFrom 與 ServivorTo 又被稱為倖存者區。
注:圖中用 S0 與 S1 指代 ServivorFrom 與 ServivorTo
新生代:主要是用來存放新生的物件。
- Eden:物件被建立的時候首先放到這個區域
- SurvivorTo:執行垃圾回收時 Eden 與 SurvivorFrom 區域存活的物件被放入到此區域
- SurvivorFrom:存活的物件被放入 SurvivorTo 之後,清空此區域,SurvivorTo 和 SurvivorFrom 的標記會互換,始終保證一個survivor是空的。
- 當籃子放不下時將存活的物件挑出來放到第二個籃子裡,並將第一個籃子清空。
- 繼續往第一個籃子中放物件,當籃子又放不下時,將第一和第二個籃子中仍存活的物件挑出來放到第三個籃子,並將第一和第二個籃子清空。
- 繼續往第一個籃子中放物件,當再次放不下時,將第一和第三個籃子中仍存活的物件放到第二個籃子中,並將第一和第三個籃子清空
- 重複上述過程,始終保證第二個籃子和第三個籃子中有一個是空的
至於為什麼按照8:1:1的大小比例來劃分新生代,主要是因為研究表明新生代中的物件98%是朝生夕死的,所以並不需要按照1:1的比例劃分記憶體空間,而是將記憶體分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用Eden和其中的一塊Survivor(可以通過虛擬機器引數 -XX:SurvivorRatio 來配置比例)。
這種垃圾收集演算法稱為複製演算法。新生代中執行的垃圾回收叫做Minor GC或Young GC,每一次Minor GC後留下來的物件 age + 1。
老年代:用於存放新生代中經過多次垃圾回收仍然存活的物件,也就是age達到了一定大小;也有可能是新生代分配不了記憶體的大物件會直接進入老年代。
檢視JVM記憶體資訊
JDK 中自帶了許多檢視和除錯 JVM 的工具,通過這些工具可以很方便的檢視當前執行緒狀態、記憶體資訊等資料。
例如,我們可以通過命令列工具jps獲取當前所有java程序的pid:
C:\>jps -l
15828 thread.Local
8676
10520 org.jetbrains.jps.cmdline.Launcher
8168 sun.tools.jps.Jps
獲得了程序的 pid 之後,就可以使用 jmap 工具輸出該程序的堆記憶體使用狀況了(僅摘取部分)。
C:\>jmap -heap 15828
Heap Configuration:
MaxHeapSize = 2109734912 (2012.0MB)
NewSize = 44040192 (42.0MB)
MaxNewSize = 703070208 (670.5MB)
OldSize = 88080384 (84.0MB)
NewRatio = 2
SurvivorRatio = 8
Heap Usage:
PS Young Generation
Eden Space:
capacity = 33554432 (32.0MB)
used = 5375456 (5.126434326171875MB)
free = 28178976 (26.873565673828125MB)
16.02010726928711% used
From Space:
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
To Space:
capacity = 5242880 (5.0MB)
used = 0 (0.0MB)
free = 5242880 (5.0MB)
0.0% used
PS Old Generation
capacity = 88080384 (84.0MB)
used = 0 (0.0MB)
free = 88080384 (84.0MB)
0.0% used
注:PS指Parallel Scavenge並行收集器,是一種吞吐量優先的垃圾回收機制。