Serial模式下的GC測試

環境:

windows7 64bit

JDK1.7.0_07

測試1:沒有手動分配任何對象時的內存狀況
VM參數:-XX:+UseSerialGC -Xmx20m -Xms20m -verbose:gc -XX:+PrintGCDetails -Xmn10m -XX:SurvivorRatio=8

使用傳統的SerialGC,最大堆內存20m,年輕代10m,Survivor與Eden之比爲1:8

即Survivor=1M,Eden=8M

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	public static void main(String[] args) {
		
	}
}

輸出:

Heap
 def new generation   total 9216K, used 835K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  10% used [0x00000000f9a00000, 0x00000000f9ad0fd0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2467K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb068f78, 0x00000000fb069000, 0x00000000fc2c0000)
No shared spaces configured.

分析:

def new generation   total 9216K, used 835K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  10% used [0x00000000f9a00000, 0x00000000f9ad0fd0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)

Eden的大小確實是所分配的8M

from和to都是Survivor,大小也都是我們所分配的1M

第一行表示eden+from的大小,即8M+1M=9M.main方法中什麼都沒有做,但年輕代中仍分配了835K的空間,說明程序運行過程中VM默默地產生了一些我們不知道的對象。

tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2467K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb068f78, 0x00000000fb069000, 0x00000000fc2c0000)
No shared spaces configured.
老年代總大小爲10M沒有問題,使用到0K。

持久代總大小爲21248K,我們沒有手動指定它的大小,這個值應該是默認值。使用了2467K,是VM啓動過程中必須加載的類佔用的空間。


測試2:持久代

import java.util.Date
public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	public static void main(String[] args) {
	        Date d = new Date();
	}
}
輸出:

Heap
 def new generation   total 9216K, used 835K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  10% used [0x00000000f9a00000, 0x00000000f9ad0fd0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2526K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb077848, 0x00000000fb077a00, 0x00000000fc2c0000)
No shared spaces configured.

分析:和測試1相比多了一個Date對象。持久代的大小也從2467到了2526,中間的差值應該就是Date類class的大小。

比較蹊蹺的是,年輕代還是隻佔用了835K。


測試3:增加Date對象的數量

import java.util.Date
public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	public static void main(String[] args) {
	       Date[] d = new Date[100];
	}
}
輸出:

Heap
 def new generation   total 9216K, used 835K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  10% used [0x00000000f9a00000, 0x00000000f9ad0fd0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2526K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb077b10, 0x00000000fb077c00, 0x00000000fc2c0000)
No shared spaces configured.
分析:毀三觀了,居然還是835K。難道Date對象不佔空間?點進去看Date類,有兩個long,一個int和一個BaseCalendar.Date對象,不科學...


測試4:繼續增加Date對象的個數

import java.util.Date
public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	public static void main(String[] args) {
	       Date[] d = new Date[10000]; 
	}
}
輸出:

Heap
 def new generation   total 9216K, used 1168K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  14% used [0x00000000f9a00000, 0x00000000f9b240f0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2527K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb077e78, 0x00000000fb078000, 0x00000000fc2c0000)
No shared spaces configured.
分析:總算變大了,說明了兩個問題。

1.Date對象還是佔空間的

2.剛開始分配的835K有蹊蹺,應該是預留了一部分空間分配給像Date這樣的小對象,這部分空間用完了才額外分配。

奇怪的是持久代居然從2526K變成了2527K,說明持久代除了存儲類信息外還存了別的東西...


測試5:分配7M空間

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	
	public static void main(String[] args) {
		alloc();
	}
	public static void alloc(){
		byte[] alloc1,alloc2,alloc3,alloc4,alloc5;
		alloc1 = new byte[2 * _1MB];
		alloc2 = new byte[2 * _1MB];
		alloc3 = new byte[2 * _1MB];
		alloc4 = new byte[1 * _1MB];
	}
}
輸出:

Heap
 def new generation   total 9216K, used 8004K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  97% used [0x00000000f9a00000, 0x00000000fa1d1010, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2467K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb068ff8, 0x00000000fb069000, 0x00000000fc2c0000)
No shared spaces configured.

分析:分配了7M的空間,年輕代被佔用了8004K,全部在Eden中,包括前面說到的835K、分配的7M和莫名其妙多出來的1K。

再分配188K就能把Eden填滿了。


測試6:分配7M + 188k空間

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	
	public static void main(String[] args) {
		alloc();
	}
	public static void alloc(){
		byte[] alloc1,alloc2,alloc3,alloc4,alloc5;
		alloc1 = new byte[2 * _1MB];
		alloc2 = new byte[2 * _1MB];
		alloc3 = new byte[2 * _1MB];
		alloc4 = new byte[1 * _1MB];
                alloc5 = new byte[188 * _1KB];
 }
}
輸出:

Heap
 def new generation   total 9216K, used 8192K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K, 100% used [0x00000000f9a00000, 0x00000000fa200000, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2468K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb069020, 0x00000000fb069200, 0x00000000fc2c0000)
No shared spaces configured.

分析:剛好填滿,再多分配1K會觸發GC嗎?


測試6:分配7M + 189k空間

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	
	public static void main(String[] args) {
		alloc();
	}
	public static void alloc(){
		byte[] alloc1,alloc2,alloc3,alloc4,alloc5;
		alloc1 = new byte[2 * _1MB];
		alloc2 = new byte[2 * _1MB];
		alloc3 = new byte[2 * _1MB];
		alloc4 = new byte[1 * _1MB];
                alloc5 = new byte[189 * _1KB];
 }
}
輸出:

Heap
 def new generation   total 9216K, used 8192K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K, 100% used [0x00000000f9a00000, 0x00000000fa200000, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2468K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb069020, 0x00000000fb069200, 0x00000000fc2c0000)
No shared spaces configured.
分析:沒有發生GC,年輕代還是8192K,應該是前面提到的835K起作用,把alloc5給吞了。

 

測試7:分配8M空間

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	
	public static void main(String[] args) {
		alloc();
	}
	public static void alloc(){
		byte[] alloc1,alloc2,alloc3,alloc4,alloc5;
		alloc1 = new byte[2 * _1MB];
		alloc2 = new byte[2 * _1MB];
		alloc3 = new byte[2 * _1MB];
		alloc4 = new byte[2 * _1MB];
	}
}
輸出:
[GC [DefNew: 6816K->467K(9216K), 0.0059008 secs] 6816K->6611K(19456K), 0.0059463 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 3007K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  31% used [0x00000000f9a00000, 0x00000000f9c7af60, 0x00000000fa200000)
  from space 1024K,  45% used [0x00000000fa300000, 0x00000000fa374e40, 0x00000000fa400000)
  to   space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 tenured generation   total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2470K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb069bb8, 0x00000000fb069c00, 0x00000000fc2c0000)
No shared spaces configured.

分析:835K裏未知大小的預留空間顯然裝不下2M的對象了,觸發GC。

GC過程如下:

1.掃描年輕代

情況a:回收不可達的對象空間。

情況b:對象可達,且to space還放得下,則把這個對象移到to space中。

情況c:對象可達,但to space已經放不下了,把這個對象移動到老年代中。

2.交換from和to

本次GC的分析:

1.回收前爲6816K,小與6M+835K,說明這835K中有一些是執行完所有用戶代碼後虛擬機產生的對象,而不全是VM啓動產生的。

2.6816K中包含3個2M用戶分配的對象,和剩下672K的VM分配的對象。672K中的467K仍然可達,被丟進to space;剩下的225K已經不可達,被回收。

3.掃描到3個2M的對象,仍然可達且to space 放不下,全部丟到老年代中。

4.交換from 和 to , GC完成。

5.2M的alloc4被丟進Eden中。

6.VM又產生了一些對象進入Eden,使得total的大小達到3007K。

從結果可知,from中的45%即是GC中年輕代剩下的467K,本來在to space中,交換後變成from。Eden的31%包括了2M的alloc4和一些VM分配的對象。

測試8:Full GC

public class GCTest {
	public static final int _1MB = 1024 * 1024;
	public static final int _1KB = 1024;
	
	public static void main(String[] args) {
		alloc();
	}
	public static void alloc(){
		byte[] alloc1,alloc2,alloc3,alloc4,alloc5;
		alloc1 = new byte[4 * _1MB];
		alloc2 = new byte[4 * _1MB];
		alloc3 = new byte[4 * _1MB];
		alloc4 = new byte[3 * _1MB];
		alloc5 = new byte[1 * _1MB];
		alloc5 = null;
		alloc5 = new byte[1 * _1MB];
	}
}
輸出
[GC [DefNew: 4768K->467K(9216K), 0.0045742 secs] 4768K->4563K(19456K), 0.0046175 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [DefNew: 4891K->467K(9216K), 0.0039990 secs] 8987K->8659K(19456K), 0.0040356 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [DefNew: 7728K->7728K(9216K), 0.0000152 secs][Tenured: 8192K->8192K(10240K), 0.0044078 secs] 15920K->15827K(19456K), [Perm : 2463K->2463K(21248K)], 0.0044738 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[Full GC [Tenured: 9216K->8192K(10240K), 0.0043707 secs] 16851K->15827K(19456K), [Perm : 2463K->2463K(21248K)], 0.0044154 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 7799K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  95% used [0x00000000f9a00000, 0x00000000fa19de10, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 9216K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  90% used [0x00000000fa400000, 0x00000000fad00030, 0x00000000fad00200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2470K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb069be8, 0x00000000fb069c00, 0x00000000fc2c0000)
No shared spaces configured.

分析:

分配alloc1,Eden空間佔用4M多一點。

分配alloc2,Eden剩下空間不足4M,發生第一次GC,alloc1被丟進老年代,alloc2進入Eden。此時老年代爲4M,年輕代爲4M多一點。

分配alloc3,情況和上一次差不多,發生第二次GC,alloc2進入老年代,alloc3進入Eden。此時老年代爲8M,年輕代爲4M多一點。

分配alloc4,直接進入年輕代,年輕代爲7M多一點。

分配alloc5,年輕代剩下不足1M,發生第三次GC,啥也沒回收掉,alloc5直接進入老年代了。

將alloc5指空後再次分配,和上次情況差不多,但這次老年代也沒空間了。發生Full GC,回收掉剛被指空的那1M空間,新的alloc5又被放進去。


總結:對GC有了理性的認識。下一章研究JDK1.7新加的G1垃圾收集器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章