前言
上回說道自動內(nèi)存管理機(jī)制,那么沒用的對象該怎么處理呢?內(nèi)存又該怎么分配侦高?為了應(yīng)對這些場景械馆,這回我們引入垃圾收集器和內(nèi)存分配策略胖眷。
基本概念
-
垃圾收集器(Garbage Collection,GC)
這并不是一門java語言的伴生產(chǎn)物,實際上比java語言早半個多世紀(jì)問世霹崎。1960年MIT的Lisp是第一門真正使用內(nèi)存動態(tài)分配和垃圾收集技術(shù)的語言珊搀。我們在上回就知道了程序計數(shù)器,虛擬機(jī)棧尾菇,本地方法棧這三個區(qū)域隨著線程而生境析,隨著線程而滅,不需要考慮回收問題派诬,線程結(jié)束劳淆,內(nèi)存就跟著回收了。但是java堆和方法區(qū)就不一樣了默赂,每個實例需要的內(nèi)存可能不一樣沛鸵,一個方法的多個分支需要的內(nèi)存也有可能不一樣,我們只有在程序處于運行期間才能知道會創(chuàng)建哪些對象缆八,分配內(nèi)存和回收都是動態(tài)的曲掰。因此,垃圾收集器關(guān)注的就是這部分內(nèi)存耀里。
-
如何確定對象是否該回收
引用計數(shù)算法
給對象添加一個引用計數(shù)器蜈缤,引用一次則加一,引用失效則減一冯挎,當(dāng)引用為0的對象則表示不能再被使用底哥。
這是個不錯的算法咙鞍,如微軟的COM技術(shù),使用ActionScript3的FlashPlayer趾徽、python語言和游戲領(lǐng)域的Squirrel都采用這個算法進(jìn)行內(nèi)存管理续滋,然而主流的java虛擬機(jī)并沒有采用引用計數(shù)算法管理內(nèi)存,主要是很難處理對象之間相互循環(huán)引用的問題孵奶。-
可達(dá)性算法
以一個GC Roots的對象為起點疲酌,從這些節(jié)點向下探索,探索走過的路徑為引用鏈了袁,當(dāng)一個對象到GC Roots沒有任何引用鏈時朗恳,證明這個對象是不可用的。
不可達(dá)對象也并非一定會被回收载绿,要真正宣布對象死亡粥诫,至少需要經(jīng)歷兩次標(biāo)記過程:如果對象進(jìn)行可達(dá)性分析發(fā)現(xiàn)沒有與GC Roots相連接,那么崭庸,它會被第一次標(biāo)記并且進(jìn)行一次篩選怀浆,篩選條件是此對象是否有必要執(zhí)行finalize()方法。對象如果沒有覆蓋該方法怕享,或者改方法已經(jīng)被虛擬機(jī)調(diào)用過执赡,則虛擬機(jī)認(rèn)為沒有必要執(zhí)行。對象將會進(jìn)入一個叫F-Queue的隊列中函筋。GC將會對隊列中的對象第二次小規(guī)模標(biāo)記沙合,如果對象不通過finalize()方法自救的話,對象將會被回收驻呐。當(dāng)然這種方式拯救對象是不推薦的灌诅,運行代價高昂,不確定性大含末,使用try-finally方式可能做得更好猜拾。
JDK中對Object.finalize()方法的解釋是
當(dāng)垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調(diào)用此方法佣盒。子類重寫 finalize 方法挎袜,以配置系統(tǒng)資源或執(zhí)行其他清除。
對于任何給定對象肥惭,Java 虛擬機(jī)最多只調(diào)用一次 finalize 方法盯仪。
我們可以理解為當(dāng)顯示的調(diào)用System.gc()時,java虛擬機(jī)會調(diào)用一次finalize()方法蜜葱,由于這個方法是protected類型的所以優(yōu)先級比較低全景,我們需要通過sleep方法來等待。
當(dāng)?shù)诙握{(diào)用System.gc()時牵囤,虛擬機(jī)是不會再次執(zhí)行finalize方法爸黄。
以下是關(guān)于finalize()方法自救的案例滞伟。
public class FinalizaEscapeGC {
public static FinalizaEscapeGC SAVE_HOKE = null;
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
System.out.println("finalize method execute ! ");
FinalizaEscapeGC.SAVE_HOKE = this;
}
public static void main(String[] args) throws InterruptedException {
SAVE_HOKE = new FinalizaEscapeGC();
SAVE_HOKE = null;
System.gc();
Thread.sleep(500);
if(SAVE_HOKE != null){
System.out.println("yes,i am still alive !");
}else{
System.out.println("no,i am dead !");
}
SAVE_HOKE = null;
System.gc();
Thread.sleep(500);
if(SAVE_HOKE != null){
System.out.println("yes,i am still alive !");
}else{
System.out.println("no,i am dead !");
}
}
}
- 回收方法區(qū)
其實 java虛擬機(jī)規(guī)范中說過可以不要求虛擬機(jī)在方法區(qū)實現(xiàn)垃圾收集,因此很多人認(rèn)為方法區(qū)(永久代)是沒有垃圾收集的炕贵。方法區(qū)進(jìn)行垃圾收集效率十分低梆奈,在堆中新生代一次垃圾收集一般可以回收70%-95%的空間,而永久代的垃圾收集效率遠(yuǎn)低于此称开。
判斷一個類是否是無用類亩钟,我們需要滿足一下三點:
a. 該類所有實例都已經(jīng)被回收
b. 該類的ClassLoader已經(jīng)被回收
c. 該類對象的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法鳖轰。
- 垃圾收集算法
-
標(biāo)記-清除算法
最基礎(chǔ)的收集算法清酥。先標(biāo)記需要回收的對象,然后統(tǒng)一回收所有標(biāo)記的對象脆霎。
缺點:效率不高总处,標(biāo)記清除后會產(chǎn)生大量的內(nèi)存碎片,碎片太多可能會導(dǎo)致需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不觸發(fā)下一次標(biāo)記-清除算法睛蛛。
-
復(fù)制算法
針對上一種算法,解決效率問題胧谈,就有了復(fù)制算法忆肾。將內(nèi)存按容量分成兩塊,每次只使用一塊菱肖。當(dāng)一塊內(nèi)存用完了客冈,就將存過的對象復(fù)制到另一塊上面,然后將使用過的內(nèi)存塊一次清理掉稳强。
缺點:內(nèi)存縮小一半场仲,代價太高。
-
標(biāo)記-清理算法
復(fù)制算法在對象存活率較高時就要進(jìn)行較多的復(fù)制操作退疫,效率會變低渠缕,更關(guān)鍵的是浪費50%空間。因此老年代一般不能直接選用這種算法褒繁。
標(biāo)記-整理算法和標(biāo)記-清除算法一樣亦鳞,但是后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓存活對象都向一端移動棒坏,然后清理掉邊端以外的內(nèi)存燕差。
- 分代收集算法
當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用這個算法。將內(nèi)存劃分為幾塊坝冕,一般吧java堆分為新生代和老年代徒探,根據(jù)每個年代的特點采用最適合的收集算法。
在新生代中喂窟,每次垃圾收集時都有大批對象死亡测暗,只有少量存活吵血,那就選用復(fù)制算法。老年代必須使用標(biāo)記-清理或者標(biāo)記-整理算法來回收偷溺。
實例
- HotSpot的算法實現(xiàn)
1.Stop The World 枚舉根節(jié)點
可達(dá)性算法以GC Roots 節(jié)點尋找引用鏈需要耗費很多時間蹋辅,可達(dá)性分析對于執(zhí)行時間的敏感體現(xiàn)在了GC停頓上。分析工作必須在一個能確保一致性的快照中進(jìn)行挫掏,即時間凍結(jié)在某個點侦另。這就是Stop The World ,即使在幾乎不會停頓的CMS收集器中尉共,枚舉根節(jié)點時也是必須停頓的褒傅。
- 安全點。
OopMap幫助HotSpot快速完成GC Roots枚舉袄友。
長時間執(zhí)行產(chǎn)生safepoint,體現(xiàn)在指令序列復(fù)用殿托,如方法調(diào)用,循環(huán)跳轉(zhuǎn)剧蚣,異常跳轉(zhuǎn)等支竹。 - 安全區(qū)域
-垃圾收集器
-
Serial 收集器
最基本、發(fā)展最悠久的收集器鸠按,曾經(jīng)虛擬機(jī)新生代收集的唯一選擇礼搁。它是一個單線程收集器。
有Stop The World的不良體驗目尖÷猓“你媽媽在給你打掃房間的時候,肯定會讓你老老實實地在椅子上或者房間外待著瑟曲,如果她一邊打掃饮戳,你一邊亂扔紙屑,這房間還能打掃完洞拨?”這個理由還是很合理的扯罐。
-
ParNew收集器
其實就是Serial收集器的多線程版本。它能做到一邊打掃扣甲,一邊讓你扔紙屑篮赢。
-
Parallel Scavenge收集器
這是一個使用復(fù)制算法的新生代收集器,也是并行的多線程收集器琉挖,它關(guān)注的是吞吐量启泣。吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)。
Serial Old 收集器
Serial收集器的老年代版本示辈,也是個單線程收集器寥茫,使用的是標(biāo)記-整理算法。Parallel Old
老年代的Parallel Scavenge
6.CMS收集器
Concurrent Mark Sweep 收集器是一種以獲取最短回收停頓時間為目標(biāo)的收集器矾麻。
基于標(biāo)記-清除算法實現(xiàn)的纱耻,運行過程較前面幾種收集器來說復(fù)雜了點芭梯,分為四個步驟即初始標(biāo)記、并發(fā)標(biāo)記弄喘、重新標(biāo)記玖喘、并發(fā)清除。其中初始標(biāo)記蘑志、重新標(biāo)記兩個步驟仍然需要Stop The World .初始標(biāo)記標(biāo)記下GC Roots能直接關(guān)聯(lián)的對象累奈,速度很快。并發(fā)標(biāo)記是進(jìn)行GC Roots Tracing過程急但,重新標(biāo)記修正并發(fā)標(biāo)記期間因用戶程序運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄澎媒,耗時遠(yuǎn)小于并發(fā)標(biāo)記時間。
CMS收集器的缺點:
a.對CPU資源敏感波桩,會降低總吞吐量
b.無法處理浮動垃圾
c.基于標(biāo)記-清除算法的弊端
7.G1收集器
這是收集器技術(shù)發(fā)展的最前沿成果之一戒努,是為取代CMS收集器而生的。特點有:并行和并發(fā)镐躲、分代收集储玫、空間整合、可預(yù)測的停頓匀油。G1收集器大致分四個步驟:初始標(biāo)記缘缚、并發(fā)標(biāo)記、最終標(biāo)記敌蚜、篩選回收。
- 內(nèi)存分配與回收策略
- 對象優(yōu)先在新生代Eden區(qū)分配窝爪。
- 大對象直接進(jìn)入老年代弛车。
大對象指:很長的字符串,數(shù)組等蒲每。在開發(fā)的時候應(yīng)該盡量避免那種短命的大對象纷跛。 - 長期存活的對象將進(jìn)入老年代。
- 實戰(zhàn)
-
開啟eclipseGC日志
其中:-XX:+PrintGCDetails為收集日志參數(shù)邀杏,-Xms20M -Xmx20M -Xmn10m 表示限制java堆20M內(nèi)存贫奠,其中分配給新生代10M,老年代剩下的10M望蜡。-XX:SurvivorRatio=8決定了新生代中Eden區(qū)與一個Survivor區(qū)的空間比例是8:1
-
理解java堆的結(jié)構(gòu)
java堆分新生代和老年代唤崭,新生代由一個Eden和兩個Survivor組成,默認(rèn)比例為8比1脖律,即我們分配給新生代10M內(nèi)存谢肾,Eden為8M,Survivor各占1M小泉。兩個Survivor用from芦疏,to 區(qū)分冕杠。
-
理解gc日志
33.125,100.667表示的是gc發(fā)生的時間酸茴,這個是虛擬機(jī)啟動以來的秒數(shù)
[GC 和 [Full GC 表示垃圾收集的停頓類型俘侠,并不是用來區(qū)分老年代和新生代。如果是Full GC 表示發(fā)生過stop the world秀睛。
[DefNew擅威,[Tenured ,[Perm 表示GC發(fā)生的區(qū)域飘诗。
方括號里面的3324K->152K(3712K) 表示GC前該內(nèi)存已使用容量->GC后該內(nèi)存已使用容量(該內(nèi)存總?cè)萘浚┯氤嚼ㄌ柾饷娴?324K->152K(11904K)表示GC前java堆已使用容量->GC后java堆已使用容量(該內(nèi)存總?cè)萘浚竺娴?.0031680 secs 表示該內(nèi)存區(qū)域GC所占用的時間昆稿,單位是秒纺座。更詳細(xì)一點的是[Times:user=0.01 sys=0.00,real=0.02 secs] 表示用戶、操作內(nèi)核溉潭、操作從開始到結(jié)束的墻鐘時間净响。 案例
a. 對象優(yōu)先存入Eden區(qū)
/*
*
* VM 參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8
*
*/
public class WatchGcDemo1 {
private static final int _1MB = 1024*1024;
public static void testAllocation(){
byte[] a1,a2,a3,a4;
a1 = new byte[2*_1MB];
a2 = new byte[2*_1MB];
a3 = new byte[2*_1MB];
a4 = new byte[4*_1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
控制臺打印
[GC [DefNew: 6487K->150K(9216K), 0.0047964 secs] 6487K->6294K(19456K), 0.0048239 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4574K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 54% used [0x330f0000, 0x33541fa8, 0x338f0000)
from space 1024K, 14% used [0x339f0000, 0x33a15b68, 0x33af0000)
to space 1024K, 0% used [0x338f0000, 0x338f0000, 0x339f0000)
tenured generation total 10240K, used 6144K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 60% used [0x33af0000, 0x340f0030, 0x340f0200, 0x344f0000)
compacting perm gen total 12288K, used 375K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454dd90, 0x3454de00, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
程序中給a4賦值的時候發(fā)生了一次新生代GC(Minor GC), [DefNew: 6487K->150K(9216K), 0.0047964 secs] 6487K->6294K(19456K), 0.0048239 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
,新生代從6487K變成150K喳瓣,而堆內(nèi)存6487K到6294K馋贤,幾乎沒有變化(虛擬機(jī)沒有找到可回收對象)。
GC發(fā)生原因是由于a4需要分配4M內(nèi)存時畏陕,發(fā)現(xiàn)Eden占6M配乓,剩余的內(nèi)存不足以分配,因此發(fā)生了Minor GC惠毁。GC期間虛擬機(jī)發(fā)現(xiàn)三個2M內(nèi)存的對象無法放入Survivor區(qū)(Survivor區(qū)只有1M內(nèi)存空間)犹芹,只好通過分配擔(dān)保機(jī)制提前轉(zhuǎn)移到老年代中去了。
這次GC結(jié)束后鞠绰,a4成功的放入到了Eden中腰埂,因此,程序結(jié)束后內(nèi)存占用情況是:Eden 4M蜈膨,Survivor空閑屿笼,老年代占用6M。
b. 大對象直接進(jìn)入老年代
首先我們設(shè)置運行參數(shù)-XX:PretenureSizeThreshold=3145728翁巍,這樣超過3M的對象會直接進(jìn)入老年代驴一。PretenureSizeThreshold只對ParNew和Serial兩種收集器生效。
示例代碼
private static final int _1MB = 1024*1024;
/**
* VM 參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
*/
public static void testPretenureThreshold(){
byte[] a = new byte[3*_1MB];
}
運行結(jié)果
Heap
def new generation total 9216K, used 507K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 6% used [0x330f0000, 0x3316ef08, 0x338f0000)
from space 1024K, 0% used [0x338f0000, 0x338f0000, 0x339f0000)
to space 1024K, 0% used [0x339f0000, 0x339f0000, 0x33af0000)
tenured generation total 10240K, used 3072K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 30% used [0x33af0000, 0x33df0010, 0x33df0200, 0x344f0000)
compacting perm gen total 12288K, used 375K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454de70, 0x3454e000, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
我們不難發(fā)現(xiàn)def new generation 新生代幾乎沒有變化曙咽,tenured generation老年代使用30%蛔趴,就是我們的a對象。
c. 長期存活的對象將進(jìn)入老年代
哪些對象該存放在新生代,哪些對象又該存放在老年代孝情,虛擬機(jī)是通過一個年齡(Age)計數(shù)器來判斷鱼蝉。如果對象在Eden出生并經(jīng)歷了一次Minor GC 后,并且可以背Survivor容納的話箫荡,將會被移入到Survivor區(qū)魁亦,對象年齡設(shè)為1。以后對象每熬過一次Minor GC年齡會增加1歲羔挡,當(dāng)增加到15歲(默認(rèn)的)洁奈,會被晉升到老年代。對象晉級年齡的閾值通過-XX:MaxTenuringThreshold=1設(shè)置绞灼。
示例代碼
/**
* 長期存活的對象將進(jìn)入老年代
* VM 參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1
*/
public static void testTenuringThreshold(){
byte[] a1,a2,a3;
a1 = new byte[_1MB/4];
a2 = new byte[4*_1MB];
a3 = new byte[4*_1MB];
a3 = null;
a3 = new byte[4*_1MB];
}
運行結(jié)果
[GC [DefNew: 4695K->406K(9216K), 0.0038531 secs] 4695K->4502K(19456K), 0.0038787 secs] [Times: user=0.00 sys=0.02, real=0.00 secs]
[GC [DefNew: 4666K->0K(9216K), 0.0007054 secs] 8762K->4502K(19456K), 0.0007278 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4260K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 52% used [0x330f0000, 0x33518fe0, 0x338f0000)
from space 1024K, 0% used [0x338f0000, 0x338f0088, 0x339f0000)
to space 1024K, 0% used [0x339f0000, 0x339f0000, 0x33af0000)
tenured generation total 10240K, used 4502K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 43% used [0x33af0000, 0x33f55ab8, 0x33f55c00, 0x344f0000)
compacting perm gen total 12288K, used 375K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454df60, 0x3454e000, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
不難看出利术,發(fā)生了兩次Minor GC ,當(dāng)?shù)谝淮伟l(fā)生GC時對象a1,256K被移入到了Survivor區(qū)(1M空間),這個時候年齡為1低矮,而我們設(shè)定的閾值為1印叁,當(dāng)?shù)诙蜧C的時候,a1達(dá)到2歲军掂,就被移入到了老年代轮蜕。from,to中為0,如果我們設(shè)置閾值為3時蝗锥,結(jié)果如下
[GC [DefNew: 4695K->406K(9216K), 0.0032536 secs] 4695K->4502K(19456K), 0.0032839 secs] [Times: user=0.00 sys=0.02, real=0.00 secs]
[GC [DefNew: 4666K->406K(9216K), 0.0007343 secs] 8762K->4502K(19456K), 0.0007595 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4666K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 52% used [0x330f0000, 0x33518fe0, 0x338f0000)
from space 1024K, 39% used [0x338f0000, 0x33955b30, 0x339f0000)
to space 1024K, 0% used [0x339f0000, 0x339f0000, 0x33af0000)
tenured generation total 10240K, used 4096K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 40% used [0x33af0000, 0x33ef0010, 0x33ef0200, 0x344f0000)
compacting perm gen total 12288K, used 375K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454df60, 0x3454e000, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
from space 1024K, 39% 跃洛,證明對象a1還在Survivor區(qū)。
d. 動態(tài)對象年齡判定
為了更好的適應(yīng)不同程序的內(nèi)存狀況终议,虛擬機(jī)并不是永遠(yuǎn)要求對象的年齡必須到到閾值才能晉升汇竭,如果Survivor空間中相同年齡的對象大小總和大于Survivor空間的一半,年紀(jì)大于或等于該年齡的對象直接進(jìn)入老年代穴张,無需打到閾值中要求的年齡韩玩。示例代碼如下
/**
* 動態(tài)對象年齡判定
* VM 參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15
*/
public static void testTenuringThreshold2(){
byte[] a1,a2,a3,a4;
a1 = new byte[_1MB/4];
a2 = new byte[_1MB/4];
a3 = new byte[4*_1MB];
a4 = new byte[4*_1MB];
a4 = null;
a4 = new byte[4*_1MB];
}
運行結(jié)果
[GC [DefNew: 4951K->662K(9216K), 0.0034462 secs] 4951K->4758K(19456K), 0.0034710 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 4922K->0K(9216K), 0.0010763 secs] 9018K->4758K(19456K), 0.0011015 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4260K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 52% used [0x330f0000, 0x33518fe0, 0x338f0000)
from space 1024K, 0% used [0x338f0000, 0x338f0088, 0x339f0000)
to space 1024K, 0% used [0x339f0000, 0x339f0000, 0x33af0000)
tenured generation total 10240K, used 4758K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 46% used [0x33af0000, 0x33f95ac8, 0x33f95c00, 0x344f0000)
compacting perm gen total 12288K, used 376K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454e060, 0x3454e200, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
e. 空間分配擔(dān)保
在發(fā)送Minor GC前,虛擬機(jī)會先檢查老年代最大 可用的連續(xù)空間是否大于新生代所有對象的總空間陆馁。如果成立,那么Minor GC可以確保是安全的合愈,否則叮贩,虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗,如果允許那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小佛析,如果大于益老,將嘗試一次Minor GC,盡管是有風(fēng)險的,如果小于或者HandlePromotionFailure設(shè)置不允許冒險寸莫,那么這時改為進(jìn)行一次Full GC捺萌。
示例代碼
/**
* 空間分配擔(dān)保
* VM 參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-HandlePromotionFailure
*
*/
public static void testHandlePromotion(){
byte[] a1,a2,a3,a4,a5,a6,a7,a8;
a1 = new byte[2*_1MB];
a2 = new byte[2*_1MB];
a3 = new byte[2*_1MB];
a1 = null;
a4 = new byte[2*_1MB];
a5 = new byte[2*_1MB];
a6 = new byte[2*_1MB];
a4 = null;
a5 = null;
a6 = null;
a7 = new byte[2*_1MB];
}
運行結(jié)果
[GC [DefNew: 6487K->150K(9216K), 0.0029545 secs] 6487K->4246K(19456K), 0.0030091 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [DefNew: 6544K->150K(9216K), 0.0005309 secs] 10640K->4246K(19456K), 0.0005594 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 2362K [0x330f0000, 0x33af0000, 0x33af0000)
eden space 8192K, 27% used [0x330f0000, 0x33318fe0, 0x338f0000)
from space 1024K, 14% used [0x338f0000, 0x33915b20, 0x339f0000)
to space 1024K, 0% used [0x339f0000, 0x339f0000, 0x33af0000)
tenured generation total 10240K, used 4096K [0x33af0000, 0x344f0000, 0x344f0000)
the space 10240K, 40% used [0x33af0000, 0x33ef0020, 0x33ef0200, 0x344f0000)
compacting perm gen total 12288K, used 376K [0x344f0000, 0x350f0000, 0x384f0000)
the space 12288K, 3% used [0x344f0000, 0x3454e1d8, 0x3454e200, 0x350f0000)
ro space 10240K, 55% used [0x384f0000, 0x38a70f00, 0x38a71000, 0x38ef0000)
rw space 12288K, 55% used [0x38ef0000, 0x395942f0, 0x39594400, 0x39af0000)
Warning: The flag -HandlePromotionFailure has been EOL'd as of 6.0_24 and will be ignored