1、jvm垃圾回收器html
jvm的垃圾回收器有serial收集器、parnew收集器、parallel scavenge收集器、serial old 收集器、parallel old收集器、cms收集器、g1收集器測試
HotSpot虛擬機所包含的收集器:spa
圖中展現了7種做用於不一樣分代的收集器,若是兩個收集器之間存在連線,則說明它們能夠搭配使用。虛擬機所處的區域則表示它是屬於新生代仍是老年代收集器。code
新生代收集器:Serial、ParNew、Parallel Scavengehtm
老年代收集器:CMS、Serial Old、Parallel Old對象
整堆收集器: G1blog
2、jvm內存分配擔保機制內存
一、指定Serial+Serial Old做爲jvm垃圾回收器
jvm啓動參數以下:
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
其中10M分配給新生代,另外10M分配給老生代。新生代中eden和survivor區的比例爲8:1,eden區所分配內存爲8M,兩個survivor區分別爲1M,新生代總可用空間爲9M。
測試代碼以下:
package handlepromotion; public class HandlePromotionFailure { private static final int _1MB = 1024 * 1024; public static void testHandlePromotionFailure() { byte[] allocation1 = new byte[2* _1MB]; byte[] allocation2 = new byte[2* _1MB]; byte[] allocation3 = new byte[2* _1MB]; byte[] allocation4 = new byte[4* _1MB]; } public static void main(String[] args) { testHandlePromotionFailure(); } }
測試結果:
[GC (Allocation Failure) [DefNew: 7127K->526K(9216K), 0.0062127 secs] 7127K->6670K(19456K), 0.0062962 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
def new generation total 9216K, used 4704K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 51% used [0x00000000fec00000, 0x00000000ff014930, 0x00000000ff400000)
from space 1024K, 51% used [0x00000000ff500000, 0x00000000ff583918, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 6144K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 60% used [0x00000000ff600000, 0x00000000ffc00030, 0x00000000ffc00200, 0x0000000100000000)
Metaspace used 2671K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
測試分析:
eden區依次建立了三個2M的對象,佔用內存6M,新生代總可用空間爲9M,eden區可用空間爲8M,因爲9M-6M=3M<4M,從而進行GC,發現survivor區空間不足。此時,JVM就啓動了內存分配的擔保機制,把這6MB的三個對象直接轉移到了老年代。此時就把新生代的空間騰出來了,而後把第四個對象(4MB)放入了eden區中。
二、指定Parallel Scavenge+Serial Old做爲jvm垃圾回收器
jvm參數將-XX:+UseSerialGC修改成-XX:+UseParallelGC
測試代碼以下:
package handlepromotion; public class HandlePromotionFailure { private static final int _1MB = 1024 * 1024; public static void testHandlePromotionFailure() { byte[] allocation1 = new byte[2* _1MB]; byte[] allocation2 = new byte[2* _1MB]; byte[] allocation3 = new byte[2* _1MB]; byte[] allocation4 = new byte[4* _1MB]; } public static void main(String[] args) { testHandlePromotionFailure(); } }
測試結果:
Heap
PSYoungGen total 9216K, used 7292K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 89% used [0x00000000ff600000,0x00000000ffd1f050,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
Metaspace used 2670K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
接下來咱們將第四個對象由4M改成3M,測試結果以下:
[GC (Allocation Failure) [PSYoungGen: 7127K->632K(9216K)] 7127K->6784K(19456K), 0.0053005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC (Ergonomics) [PSYoungGen: 632K->0K(9216K)] [ParOldGen: 6152K->6669K(10240K)] 6784K->6669K(19456K), [Metaspace: 2664K->2664K(1056768K)], 0.0082392 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] Heap PSYoungGen total 9216K, used 3154K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 38% used [0x00000000ff600000,0x00000000ff914930,0x00000000ffe00000) from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 10240K, used 6669K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) object space 10240K, 65% used [0x00000000fec00000,0x00000000ff283538,0x00000000ff600000) Metaspace used 2671K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K
測試分析:
經過和上面的測試結果對比,咱們發現當第四個對象沒法放入eden區時,此時不會直接進行GC,而是判斷若是要分配的內存>=eden區的一半,那麼會直接把要分配的內存放入老年代中。不然進行GC操做,若是survivor區空間不足,則把eden區中的6M的對象直接放入老年代區中,eden區新建立4M的對象。
那麼爲何3M的時候出現了FullGC呢,原做者也給出了他的答案(https://cloud.tencent.com/developer/article/1082687)。
大概的意思是這種狀況下偶爾會發生FullGC,至於緣由大概是在使用Parallel Scavenge收集器時,jvm會進行預測,若是晉升到老年代的平均大小大於老年代剩餘大小,則認爲須要一次FullGC。當虛擬機估算出下次分配可能會發生沒法分配的問題時,則會提早發生一次FullGC。
那麼針對上面的例子,當gc操做把eden區的6M所有晉升到老年代區中,此時老年代區的大小爲10M-6M=4M,jvm預測下一次晉升時老年代空間可能不足,因而提早進行了一次FullGC。
簡單驗證下,修改jvm參數爲-Xms24M -Xmx24M -Xmn10M,將heap內存加大,新生代不變,此時老年代大小爲14M。
此時測試結果以下,未發生FullGC:
[GC (Allocation Failure) [PSYoungGen: 7127K->632K(9216K)] 7127K->6784K(23552K), 0.0043651 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap PSYoungGen total 9216K, used 3786K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) eden space 8192K, 38% used [0x00000000ff600000,0x00000000ff914930,0x00000000ffe00000) from space 1024K, 61% used [0x00000000ffe00000,0x00000000ffe9e030,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 14336K, used 6152K [0x00000000fe800000, 0x00000000ff600000, 0x00000000ff600000) object space 14336K, 42% used [0x00000000fe800000,0x00000000fee02030,0x00000000ff600000) Metaspace used 2671K, capacity 4486K, committed 4864K, reserved 1056768K class space used 288K, capacity 386K, committed 512K, reserved 1048576K