JAVA垃圾收集器之Serial收集器
1、特點
Serial收集器是JAVA虛擬機器中最基本、曆史最悠久的收集器,在JDK 1.3.1之前是JAVA虛擬機器新生代收集的唯一選擇。Serial收集器是一個單線程的收集器,但它的“單線程”的意義並不僅僅是說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的背景工作執行緒,直到它收集結束。
要是伺服器每運行一個小時就會暫停5分鐘,老闆會有什麼樣的心情?
2、發展
從JDK 1.3開始,一直到現在還沒正式發布的JDK 1.7,HotSpot虛擬機器Team Dev為消除或減少背景工作執行緒因記憶體回收而導致停頓的努力一直在進行著,從Serial收集器到Parallel收集器,再到Concurrent Mark Sweep(CMS收集器)現在還未正式發布的Garbage First(G1)收集器,我們看到了一個個越來越優秀(也越來越複雜)的收集器的出現,使用者線程的停頓時間在不斷縮短,但是仍然沒有辦法完全消除。
3、現狀
Serial收集器到現在為止,它依然是JAVA虛擬機器運行在Client模式下的預設新生代收集器。它也有著優於其他收集器的地方:簡單而高效(與其他收集器的單線程比),對於限定單個CPU的環境來說,Serial收集器由於沒有線程互動的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。在使用者的案頭應用情境中,分配給虛擬機器管理的記憶體一般來說不會很大,收集幾十兆甚至一兩百兆的新生代(僅僅是新生代使用的記憶體,案頭應用基本上不會再大了),停頓時間完全可以控制在幾十毫秒最多一百多毫秒以內,只要不是頻繁發生,這點停頓是可以接受的。所以,Serial收集器對於運行在Client模式下的虛擬機器來說是一個很好的選擇。
4、代碼和樣本(固定JAVA虛擬機器的大小)
package com.gc;
import java.util.ArrayList;
import java.util.List;
/**
* 簡單的JAVA虛擬機器記憶體回收,serial收集器的使用
* 參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
public class EasySerial {
public byte[] placeHolder =new byte[64 * 1024]; //預留位置
public static voidmain(String[] args) throws Exception{
outOfMemoryByFixSize();
}
/**
* 固定JAVA虛擬機器的大小
* 參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
private static voidoutOfMemoryByFixSize() throws Exception{
List<EasySerial>list = new ArrayList<EasySerial>();
while(true){
EasySerialserial = new EasySerial();
list.add(serial);
Thread.sleep(10);//停頓10毫秒
}
}
/**
* JAVA虛擬機器的大小適當可擴充,其中Xms30m,Xmx40m
* 參數:-Xms30m -Xmx40m-XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
private static voidoutOfMemoryByExpansionSize() throws Exception{
List<EasySerial>list = new ArrayList<EasySerial>();
while(true){
EasySerialserial = new EasySerial();
list.add(serial);
Thread.sleep(10);//停頓10毫秒
}
}
}
參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
-XX:+UseSerialGC的是Serial收集器,Xms30m -Xmx30m指定了JAVA虛擬機器的固定大小為30M,-Xmn10m指JAVA新生代的空間為10M。
運行結果如下:
[GC [DefNew: 8137K->1023K(9216K),0.0054427 secs] 8137K->8019K(29696K), 0.0054763 secs] [Times: user=0.00sys=0.00, real=0.01 secs]
[GC [DefNew: 9175K->963K(9216K),0.0056574 secs] 16170K->16151K(29696K), 0.0056820 secs] [Times: user=0.00sys=0.00, real=0.00 secs]
[GC [DefNew: 9149K->9149K(9216K),0.0000197 secs][Tenured: 15188K->20441K(20480K), 0.0065753 secs]24337K->24284K(29696K), [Perm : 2086K->2086K(12288K)], 0.0066451 secs][Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC [Tenured:20441K->20441K(20480K), 0.0038802 secs] 29570K->29537K(29696K), [Perm :2086K->2086K(12288K)], 0.0039147 secs] [Times: user=0.00 sys=0.00, real=0.00secs]
[Full GC [Tenured:20441K->20436K(20480K), 0.0061865 secs] 29537K->29532K(29696K), [Perm :2086K->2084K(12288K)], 0.0062235 secs] [Times: user=0.00 sys=0.00, real=0.01secs]
Exception in thread "main"java.lang.OutOfMemoryError: Java heap space
atcom.gc.EasySerial.<init>(EasySerial.java:12)
atcom.gc.EasySerial.outOfMemoryByFixSize(EasySerial.java:25)
atcom.gc.EasySerial.main(EasySerial.java:14)
Heap
def new generation total 9216K, used 9152K [0x03bd0000,0x045d0000, 0x045d0000)
eden space 8192K, 100% used [0x03bd0000, 0x043d0000, 0x043d0000)
from space 1024K, 93% used[0x043d0000, 0x044c00f0, 0x044d0000)
to space 1024K, 0% used [0x044d0000, 0x044d0000, 0x045d0000)
tenured generation total 20480K, used 20436K [0x045d0000,0x059d0000, 0x059d0000)
the space 20480K, 99% used[0x045d0000, 0x059c52b8, 0x059c5400, 0x059d0000)
compacting perm gen total 12288K, used 2105K [0x059d0000,0x065d0000, 0x099d0000)
…
GC [DefNew :是在新生代的回收,能夠很清楚的看到新生代的空間被迅速用完。
Full GC [Tenured是在老年代的全GC回收,空間也很快用完,然後出現OutOfMemoryError錯誤,導致系統崩潰。
5、代碼和樣本(可擴充JAVA虛擬機器大小)
package com.gc;
import java.util.ArrayList;
import java.util.List;
/**
* 簡單的JAVA虛擬機器記憶體回收,serial收集器的使用
* 參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
public class EasySerial {
public byte[] placeHolder =new byte[64 * 1024]; //預留位置
public static voidmain(String[] args) throws Exception{
outOfMemoryByExpansionSize();
}
/**
* 固定JAVA虛擬機器的大小
* 參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
private static voidoutOfMemoryByFixSize() throws Exception{
List<EasySerial>list = new ArrayList<EasySerial>();
while(true){
EasySerialserial = new EasySerial();
list.add(serial);
Thread.sleep(10);//停頓10毫秒
}
}
/**
* JAVA虛擬機器的大小適當可擴充,其中Xms30m,Xmx40m
* 參數:-Xms30m -Xmx40m-XX:+UseSerialGC -XX:+PrintGCDetails
* @author 範芳銘
*/
private static voidoutOfMemoryByExpansionSize() throws Exception{
List<EasySerial>list = new ArrayList<EasySerial>();
while(true){
EasySerialserial = new EasySerial();
list.add(serial);
Thread.sleep(10);//停頓10毫秒
}
}
}
參數:-Xms30m -Xmx30m-Xmn10m -XX:+UseSerialGC -XX:+PrintGCDetails
-XX:+UseSerialGC的是Serial收集器,Xms30m –Xmx40m指定了JAVA虛擬機器的大小可以從30M提升到40M,沒有強制指定JAVA新生代的空間大小。
運行後情況:
[GC [DefNew: 1986K->128K(2112K),0.0011191 secs] 27809K->27808K(30528K), 0.0011425 secs] [Times: user=0.00sys=0.01, real=0.00 secs]
[GC [DefNew: 1989K->131K(2112K),0.0011326 secs][Tenured: 29536K->29539K(29568K), 0.0032603 secs]29669K->29667K(31680K), [Perm : 2086K->2086K(12288K)], 0.0044714 secs][Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 2562K->192K(2880K),0.0024077 secs] 32102K->32100K(40704K), 0.0024426 secs] [Times: user=0.00sys=0.00, real=0.00 secs]
[GC [DefNew: 2755K->192K(2880K),0.0015362 secs] 34663K->34662K(40704K), 0.0015731 secs] [Times: user=0.00sys=0.00, real=0.00 secs]
[GC [DefNew: 2755K->192K(2880K),0.0015842 secs] 37224K->37223K(40704K), 0.0016187 secs] [Times: user=0.00sys=0.00, real=0.00 secs]
[GC [DefNew: 2755K->2755K(2880K),0.0000144 secs][Tenured: 37030K->37799K(37824K), 0.0039907 secs]39786K->39784K(40704K), [Perm : 2086K->2086K(12288K)], 0.0040547 secs][Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [Tenured:37799K->37799K(37824K), 0.0032501 secs] 40554K->40553K(40704K), [Perm :2086K->2086K(12288K)], 0.0032940 secs] [Times: user=0.00 sys=0.00, real=0.00secs]
[Full GC [Tenured:37799K->37792K(37824K), 0.0107478 secs] 40553K->40546K(40704K), [Perm :2086K->2084K(12288K)], 0.0107782 secs] [Times: user=0.02 sys=0.00, real=0.01secs]
Exception in thread "main"java.lang.OutOfMemoryError: Java heap space
atcom.gc.EasySerial.<init>(EasySerial.java:12)
atcom.gc.EasySerial.outOfMemoryByExpansionSize(EasySerial.java:39)
atcom.gc.EasySerial.main(EasySerial.java:14)
Heap
def new generation total 2880K, used 2810K [0x03ca0000,0x03fb0000, 0x03fb0000)
eden space 2624K, 99% used[0x03ca0000, 0x03f2e8e8, 0x03f30000)
from space 256K, 75% used[0x03f70000, 0x03fa0030, 0x03fb0000)
to space 256K, 0% used [0x03f30000, 0x03f30000, 0x03f70000)
tenured generation total 37824K, used 37792K [0x03fb0000,0x064a0000, 0x064a0000)
the space 37824K, 99% used[0x03fb0000, 0x064982e0, 0x06498400, 0x064a0000)
compacting perm gen total 12288K, used 2105K [0x064a0000,0x070a0000, 0x0a4a0000)
the space 12288K, 17% used [0x064a0000, 0x066ae4a8, 0x066ae600,0x070a0000)
其他的內容和固定大小基本一致,多出來一種情況:
[GC [DefNew: 2755K->2755K(2880K),0.0000144 secs][Tenured: 37030K->37799K(37824K), 0.0039907 secs]39786K->39784K(40704K), [Perm : 2086K->2086K(12288K)], 0.0040547 secs][Times: user=0.00 sys=0.00, real=0.00 secs]
這個是對新生代,老年代,永久代都進行了次級記憶體回收。