(入門貼)JVM堆記憶體相關的啟動引數:年輕代、老年代和永久代的記憶體分配
如果想觀察JVM程序佔用的堆記憶體,可以通過命令工具jmap或者視覺化工具jvisualvm.exe。JVM這些啟動引數都擁有預設值,如果想了解JVM的記憶體分配策略,最好手動設定這些啟動引數。再通過JDK提供的工具的統計結果,進行對比,就比較容易理解這些記憶體分配的理論知識。執行環境是win7 32位作業系統,JDK1.7.0_60版本。
測試程式碼和JVM啟動引數如下:
public class Test { public static void main(String[] args) { int a = 0; while (true) { a++; } } }
-Xms200M -Xmx200M -XX:NewSize=100M -Xmn100M -XX:SurvivorRatio=8
-XX:OldSize=60M -XX:PermSize=50M -XX:MaxPermSize=50M
執行上述程式碼,通過jps命令獲取到程序pid,然後通過jmap -heap pid就可以檢視記憶體分配和使用情況。
>jmap -heap 8912 Attaching to process ID 8912, please wait... Debugger attached successfully. Client compiler detected. JVM version is 24.60-b09 using thread-local object allocation. Mark Sweep Compact GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 209715200 (200.0MB) NewSize = 104857600 (100.0MB) MaxNewSize = 104857600 (100.0MB) OldSize = 62914560 (60.0MB) NewRatio = 3 SurvivorRatio = 8 PermSize = 52428800 (50.0MB) MaxPermSize = 52428800 (50.0MB)
這裡顯示的堆配置引數,都可以通過JVM啟動引數來設定。下面來解釋下幾個重要引數的含義:
-Xms 和 -Xmx (-XX:InitialHeapSize 和 -XX:MaxHeapSize):指定JVM初始佔用的堆記憶體和最大堆記憶體。JVM也是一個軟體,也必須要獲取本機的物理內
存,然後JVM會負責管理向作業系統申請到的記憶體資源。JVM啟動的時候會向作業系統申請 -Xms 設定的記憶體,JVM啟動後執行一段時間,如果發現記憶體空間
不足,會再次向作業系統申請記憶體。JVM能夠獲取到的最大堆記憶體是-Xmx設定的值。
-XX:NewSize 和 -Xmn(-XX:MaxNewSize):
-XX:SurvivorRatio:設定新生代中1個Eden區與1個Survivor區的大小比值。在hotspot虛擬機器中,新生代 = 1個Eden + 2個Survivor。如果新生代記憶體是
10M,SurvivorRatio=8,那麼Eden區佔8M,2個Survivor區各佔1M。
-XX:NewRatio:指定老年代/新生代的堆記憶體比例。在hotspot虛擬機器中,堆記憶體 = 新生代 + 老年代。如果-XX:NewRatio=4表示年輕代與年老代所佔比值為1:4,年輕代佔整個堆記憶體的1/5。在設定了-XX:MaxNewSize的情況下,-XX:NewRatio的值會被忽略,老年代的記憶體=堆記憶體 - 新生代記憶體。老年代的最大記憶體 = 堆記憶體 - 新生代 最大記憶體。
-XX:OldSize:設定JVM啟動分配的老年代記憶體大小,類似於新生代記憶體的初始大小-XX:NewSize。
-XX:PermSize 和 -XX:MaxPermSize:指定JVM中的永久代(方法區)的大小。可以看到:永久代不屬於堆記憶體,堆記憶體只包含新生代和老年代。
可以發現:堆記憶體、新生代記憶體、老年代記憶體、永久代記憶體,都有一個初始記憶體,還有一個最大記憶體。下面以老年代的初始記憶體和最大記憶體為例,看下記憶體變化的效果,其他的應該類似。測試程式碼如下:
public class TurnedTest
{
private static List<String> list = new ArrayList<String>();
public static void main(String[] args)
{
int a = 0;
while (true)
{
a++;
list.add("demo");
}
}
}
顯然這個程式存在記憶體洩露,最終會佔滿整個堆記憶體,丟擲OOM。為了看清楚這個演變的過程,我們在while迴圈中新增一個斷點,設定breakpoint properties中的"hit count"為100000,以debug模式執行上面的程式,然後使用jmap觀察記憶體佔用情況。
tenured generation:
capacity = 62914560 (60.0MB)
used = 0 (0.0MB)
free = 62914560 (60.0MB)
0.0% used
tenured generation:
capacity = 62914560 (60.0MB)
used = 16409080 (15.648918151855469MB)
free = 46505480 (44.35108184814453MB)
26.08153025309245% used
tenured generation:
capacity = 62914560 (60.0MB)
used = 53329496 (50.858970642089844MB)
free = 9585064 (9.141029357910156MB)
84.76495107014973% used
tenured generation:
capacity = 104857600 (100.0MB)
used = 84217880 (80.3164291381836MB)
free = 20639720 (19.683570861816406MB)
80.3164291381836% used
可以發現老年代記憶體從最開始的60M,擴大到最大值100M。