1.概念解釋
1.1 年青代=新生代(eden space)+2個survivor
年青代用來存放新近創建的對象,尺寸隨堆大小的增大和減小而相應的變化,默認值是保持爲堆大小的1/15,可以通過-Xmn參數設置年青代爲固定大小,也可以通過-XX:NewRatio來設置年青代與年老代的大小比例,年青代的特點是對象更新速度快,在短時間內產生大量的“死亡對象”。年輕代的特點是產生大量的死亡對象,並且要是產生連續可用的空間, 所以使用複製清除算法和並行收集器進行垃圾回收. 對年輕代的垃圾回收稱作初級回收 (minor gc)
初級回收將年輕代分爲三個區域, 一個新生代 , 2個大小相同的復活代, 應用程序只能使用一個新生代和一個復活代, 當發生初級垃圾回收的時候,gc掛起程序, 然後將新生代和復活代中的存活對象複製到另外一個非活動的復活代中,然後一次性清除新生代和復活代,將原來的非復活代標記成爲活動復活代. 將在指定次數回收後仍然存在的對象移動到年老代中, 初級回收後,得到一個空的可用的新生代.
1.2
所謂的新生代和老年代是針對於分代收集算法來定義的,新生代又分爲Eden和Survivor兩個區。加上老年代就這三個區。數據會首先分配到Eden區當中(當然也有特殊情況,如果是大對象那麼會直接放入到老年代(大對象是指需要大量連續內存空間的java對象)。),當Eden沒有足夠空間的時候就會觸發jvm發起一次Minor GC。如果對象經過一次Minor GC還存活,並且又能被Survivor空間接受,那麼將被移動到Survivor空間當中。並將其年齡設爲1,對象在Survivor每熬過一次Minor GC,年齡就加1,當年齡達到一定的程度(默認爲15)時,就會被晉升到老年代中了,當然晉升老年代的年齡是可以設置的。
其實新生代和老年代就是針對於對象做分區存儲,更便於回收等等。
2.相關的啓動參數:年輕代、老年代和永久代的內存分配
如果想觀察JVM進程佔用的堆內存,可以通過命令工具jmap或者可視化工具jvisualvm.exe。JVM這些啓動參數都擁有默認值,如果想了解JVM的內存分配策略,最好手動設置這些啓動參數。再通過JDK提供的工具的統計結果,進行對比,就比較容易理解這些內存分配的理論知識。運行環境是win7 32位操作系統,JDK1.7.0_60版本。
測試代碼和JVM啓動參數如下:
1
2
3
4
5
6
7
8
9
10
11
|
public
class Test { public
static void
main(String[] args) { int
a = 0 ; while
( true ) { a++; } } } |
1
2
|
-Xms=200M -Xmx200M -XX:NewSize=100M -Xmn100M -XX:SurvivorRatio= 8 -XX:OldSize=60M -XX:PermSize=50M -XX:MaxPermSize=50M |
運行上述代碼,通過jps命令獲取到進程pid,然後通過jmap -heap pid就可以查看內存分配和使用情況。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>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):指定JVM啓動時分配的新生代內存和新生代最大內存。
-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中的永久代(方法區)的大小。可以看到:永久代不屬於堆內存,堆內存只包含新生代和老年代。
可以發現:堆內存、新生代內存、老年代內存、永久代內存,都有一個初始內存,還有一個最大內存。下面以老年代的初始內存和最大內存爲例,看下內存變化的效果,其他的應該類似。測試代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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" ); } } }</string></string> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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 |