一简僧、java對(duì)象分配策略
java中所說(shuō)的自動(dòng)內(nèi)存管理最終可以歸結(jié)到兩個(gè)問(wèn)題:
- 自動(dòng)分配不存
- 自動(dòng)回收內(nèi)存
對(duì)象的內(nèi)存分配主要是在堆上進(jìn)行牍帚,堆根據(jù)對(duì)象不同的存活周期分為不同的區(qū)域塞蹭,新生對(duì)象一般分在了Eden區(qū)域蕴掏,如果啟動(dòng)了線程分配緩沖,則優(yōu)先會(huì)分配到TLAB上伴奥。有少數(shù)情況新生對(duì)象會(huì)直接分配到老年代區(qū)域。實(shí)際情況要根據(jù)虛擬機(jī)模式和收集器組合來(lái)確定咕幻。
以下結(jié)論是Client模式下配合Serial和Serial Old收集器渔伯。
1、對(duì)象優(yōu)先分配在Eden區(qū)域
在多數(shù)情況下肄程,對(duì)象優(yōu)先分配在Eden區(qū)域锣吼,當(dāng)Eden區(qū)域的沒(méi)有足夠的內(nèi)存分配時(shí),虛擬機(jī)將發(fā)生一次minor GC蓝厌。
以下代碼測(cè)試新生對(duì)象的分配機(jī)制玄叠。設(shè)置堆大小為20M,不可擴(kuò)展拓提,新生代大小為10M读恃,老年代10M,Eden和from survivor和to survivor的比例為8:1:1代态。
public class TestAllocation {
private static final int _1MB = 1024;
//VM參數(shù) -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
public static void testAllocation(){
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
在進(jìn)行allocation1 寺惫,allocation12, allocation13時(shí)對(duì)象正常分配到Eden區(qū)蹦疑,在分配第四個(gè)對(duì)象的時(shí)候發(fā)現(xiàn)Eden的空間不夠用西雀,觸發(fā)一次Minor GC,minor GC過(guò)后對(duì)象1,2,3正常情況下應(yīng)該被復(fù)制算法復(fù)制到另一塊survivor空間歉摧,但是由于survivor空間不夠所以通過(guò)分配擔(dān)保將123轉(zhuǎn)移到了老年代空間中艇肴,此時(shí)Eden和兩塊survivor空閑腔呜,老年代空間占用6MB。最后第四個(gè)對(duì)象成功分配到Eden空間再悼。
(在java8中運(yùn)行上述代碼核畴,沒(méi)有出現(xiàn)相同的結(jié)果。冲九。谤草。。莺奸。)
2咖刃、大對(duì)象直接進(jìn)入老年代
大對(duì)象的存儲(chǔ)需要大量的連續(xù)的內(nèi)存空間,這對(duì)虛擬機(jī)的內(nèi)存管理來(lái)說(shuō)不是一個(gè)好消息憾筏。虛擬機(jī)設(shè)置了-XX:PretenureSizeThreshold做標(biāo)志嚎杨,當(dāng)新生對(duì)象大小超過(guò)了-XX:PretenureSizeThreshold設(shè)置的大小則獨(dú)享直接分配在老年代中。這樣做的好處是避免了在新生代中大量的復(fù)制(新生代采用的是復(fù)制算法)氧腰。壞處是增加了老年代的垃圾收集任務(wù)枫浙。且老年代的垃圾收集速度要比新生代耗時(shí)長(zhǎng)很多。
3古拴、長(zhǎng)期存活的對(duì)象進(jìn)入老年代
虛擬機(jī)為每一個(gè)對(duì)象定義了一個(gè)對(duì)象年齡計(jì)數(shù)器箩帚,如果在出生在Eden經(jīng)過(guò)一個(gè)Minor GC后存活且被survivor容納,則age設(shè)為1黄痪。對(duì)象在survivor每熬過(guò)一次Minor GC則age加1紧帕,當(dāng)age增加到15時(shí)晉升到老年代中。晉升age可以調(diào)節(jié)桅打,參數(shù)為-XX:MaxTenuringThreshold是嗜。
虛擬機(jī)并不是永遠(yuǎn)要求對(duì)象的年齡達(dá)到-XX:MaxTenuringThreshold之后才能晉升到老年代。如果在Survivor空間的相同年齡所有對(duì)象大小的總和超過(guò)了Survivor空間的一半挺尾,則虛擬機(jī)就允許大于或等于該年齡的對(duì)象直接進(jìn)入老年代鹅搪。
二、 其他概念總結(jié)
1遭铺、GC分類
GC按照發(fā)生位置分為Minor GC和Major GC:
- Minor GC:發(fā)生在新生代丽柿。由于新生代對(duì)象的特點(diǎn),Minor GC發(fā)生較頻繁魂挂,速度也很快甫题。
- Major GC/Full GC:發(fā)生在老年代。Major GC一般會(huì)伴隨多次的Minor GC涂召,速度也較Minor GC慢出一個(gè)數(shù)量級(jí)坠非。
2、空間分配擔(dān)保
在新生代中使用的收集算法是復(fù)制算法芹扭,一般將新生代分成三個(gè)區(qū)域麻顶,Eden區(qū),from Survivor和to Survivor舱卡,這三個(gè)區(qū)域的比大小例是8:1:1辅肾。一半新生對(duì)象沒(méi)有特殊設(shè)置的話會(huì)直接出生在Eden區(qū)域,如果Eden區(qū)域空間不夠則會(huì)發(fā)生一次Minor GC轮锥。如果在Minor GC時(shí)Survivor空間不夠這就需要分配擔(dān)保矫钓。
分配擔(dān)保就是Minor GC過(guò)程中Survivor區(qū)域不足以容納Eden區(qū)的存活對(duì)象從而向老年代借一塊區(qū)域用于存放存活對(duì)象。
老年代在執(zhí)行分配擔(dān)保時(shí)會(huì)有一些執(zhí)行機(jī)制舍杜。
1新娜、在Minor GC前虛擬機(jī)會(huì)檢查老年代的最大可用連續(xù)空間是否大于新生代所有對(duì)象總空間,如果大于既绩,則分配擔(dān)保安全概龄。
2、如果老年代最大連續(xù)總空間小于新生代所有對(duì)象之和饲握。虛擬機(jī)檢查HandlePromotionFailure設(shè)置是否允許分配擔(dān)保失敗私杜,如果允許。繼續(xù)檢查老年代最大連續(xù)空間是否大于歷次晉升到老年代對(duì)象的平均大小救欧。如果大于衰粹,虛擬機(jī)將允許發(fā)生Minor GC,如果小于笆怠,HandlePromotionFailure設(shè)置為不允許铝耻,此時(shí)需要進(jìn)行一次Full GC。
(JDK 6 Update 24之后的規(guī)則變?yōu)橹灰夏甏倪B續(xù)空間大于新生代對(duì)象總大小或者歷次晉升的平均大小就會(huì)進(jìn)行Minor GC蹬刷,否則將進(jìn)行Full GC瓢捉。)