第三章 垃圾回收器與內(nèi)存分配策略

1.Copying算法補充

目前主流虛擬機對新生代的對象回收都是用的這種算法匾效。IBM研究表明感论,新生代中的對象98%都是“朝生夕死”的乐横,所以并不需要按照1: 1來劃分內(nèi)存,而是將內(nèi)存分為一塊較大的Eden和兩塊較小的Survivor镇匀,每次使用使用Eden和一塊Survivor。當回收時闷袒,將Eden和Survivor中活著的對象全部復制到另一塊Survivor中坑律,然后清理掉Eden和原先的那一塊Survivor中全部的對象。HotSpot虛擬機默認的分配空間是8: 1,當然我們沒有辦法保證每次回收都只有不多于10%的對象存活晃择,當Survivor空間不夠用時冀值,需要依賴老年代進行分配擔保。

2. 內(nèi)存分配與回收策略

2.1 對象優(yōu)先在Eden分配

大多數(shù)情況下宫屠,對象在新生代Eden中分配列疗,當Eden區(qū)沒有空間時,虛擬機會發(fā)起一次MinorGC(在新生代中的垃圾回收動作)浪蹂。來看書中提供的這個例子

/**
 * Created by cwj on 17-6-3.
 * VM:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
 */
public class TestMinorGC {

    public static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        byte[] allocatioon1,allocatioon2,allocatioon3,allocatioon4;

        allocatioon1 = new byte[2 * _1MB];
        allocatioon2 = new byte[2 * _1MB];
        allocatioon3 = new byte[2 * _1MB];

        allocatioon4 = new byte[4 * _1MB];
    }
}

我使用的是idea+jdk1.8抵栈,所以與書中不同的是,要設置虛擬機使用Serial/Serial Old收集器坤次,-XX:+UseSerialGC古劲,如果不設置的話,idea會使用PS回收器缰猴,這樣效果就不一樣了产艾。看看執(zhí)行效果

執(zhí)行效果

代碼中滑绒,嘗試分配3個2M大小和1個4M大小的對象闷堡,運行時通過設置虛擬機參數(shù)將堆大小定位20M且不可擴展,其中新/老生代各占10M疑故。并且定義了Eden與Survivor比例是8:1杠览,從輸出結(jié)果也能看到:
eden space 8192K, from space 1024K, to space 1024K,其中新生代總共可用空間是9216KB(Eden+一個Survivor)纵势。
  執(zhí)行到分配allocatioon4的空間是發(fā)生了一次MinorGC踱阿,結(jié)果是新生代從7581kb變到336kb,而總內(nèi)存是7518kb變到6480kb吨悍,幾乎沒怎么變扫茅,因為1,2,3這三個對象都是存活的,并沒有被回收育瓜。這次GC發(fā)生的原因是給4分配內(nèi)存的時候,發(fā)現(xiàn)Eden已經(jīng)被占用了6M葫隙,剩余的空間不足以分配4所需要的4M空間,所以發(fā)生了MinorGC躏仇。期間虛擬機又發(fā)現(xiàn)無法將3個2M大小的對象全部放入Survivor空間(因為Survivor只有1M)恋脚,所以只能通過擔保機制,將3個對象轉(zhuǎn)移到老年代中焰手。
  這次GC結(jié)束后糟描,4M的allocation4對象被順利的分配到了Eden中,因此程序執(zhí)行結(jié)果是Eden占用了4M书妻,Survivor空閑船响,老年代被占用了6M。

2.2 大對象直接進入老年代

所謂的大對象是指需要連續(xù)大量內(nèi)存空間的Java對象,最典型的大對象就是那種很長的字符串和數(shù)組见间。經(jīng)常出現(xiàn)大對象容易導致內(nèi)存還有不少空間時就提前出發(fā)垃圾回收器以獲得足夠多的連續(xù)空間來安置他們
  虛擬機提供了一個 -XX:PretenureSizeThreshold 參數(shù)聊闯,大于這個參數(shù)的對象直接進入老年代。這樣做的目的是為了避免Eden和兩個Survivor之間發(fā)生大量的內(nèi)存復制米诉。

/**
 * Created by cwj on 17-6-3.
 * VM:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 *    -XX:PretenureSizeThreshold=3145728
 *
 */
public class TestMinorGC {

    public static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {
        byte[] allocatioon;
        allocatioon = new byte[4 * _1MB];
    }

}
執(zhí)行結(jié)果.png

結(jié)果很清楚菱蔬,因為我們預先設置了參數(shù)-XX:PretenureSizeThreshold=3145728,所以大于3M的對象直接就被分到了老年代中史侣,所以老年代占了4M拴泌,新生代空閑。

2.3 長期存活的對象將進入老年代

虛擬機采用了分代收集的思想來管理內(nèi)存惊橱,那么內(nèi)存回+收時就必須知道哪些對象應該放在新生代蚪腐,哪些放在老年代。為了做到這點税朴,虛擬機給每個對象定義了一個年齡計數(shù)器削茁。如果對象在Eden出生并經(jīng)過一次GC后仍然存活,并且能被Survivor容納的話掉房,將被移動到Survivor空間中,并且對象年齡設置為1慰丛。對象在Survivor中每熬過一次GC卓囚,年齡就增大一歲,當它的年齡增大到一定歲數(shù)時(默認15歲)诅病,就會被移到老年代哪亿。當然這個默認值可以通過修改參數(shù) -XX:MaxTenuringThreshold的值來改變。

/**
 * Created by cwj on 17-6-3.
 *
 * VM:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 *    -XX:MaxTenuringThreshold=1
 *    -XX:+PrintTenuringDistribution
 */
public class TestTenuringThreshold {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] allocation1,allocation2,allocation3;

        allocation1 = new byte[_1MB / 4];
        allocation2 = new byte[4 * _1MB];
        allocation3 = new byte[4 * _1MB];
        //第一次GC

        allocation3 = null;
        allocation3 = new byte[4 * _1MB];
        //第二次GC
    }
}

執(zhí)行結(jié)果

我們分析一下:首先1,2都被分到Eden中贤笆,當3第一次來時蝇棉,Eden不夠4M了,所以進行一次GC芥永,將1放入Survivor篡殷,Age+1。又因為Survivor只有1M埋涧,不夠4M板辽,所以直接擔保機制,2放入老年代棘催。所以Eden從5726k->616k劲弦,總內(nèi)存基本沒變。
  當?shù)诙?進來時醇坝,發(fā)生第二次GC邑跪,將原來存在Eden中的3釋放(賦值為null了),Survivor中的1已經(jīng)1歲了,所以存入老年代,這時Eden剛釋放完3画畅,下一個3還沒有進來砸琅,所以Eden從4712k->0,總內(nèi)存由8808k->4706k,GC過后將新的3存入Eden夜赵。兩次GC過后明棍。Eden中存著的是3,老年代中存著的是2和1寇僧。
  如果將參數(shù) -XX:MaxTenuringThreshold值改為15摊腋,兩次GC過后應該是Eden中存著3,Survivor中存著1嘁傀,老年代中存著2.(理論是這樣兴蒸,然而我的idea中跑的結(jié)果還是跟參數(shù)為1的情況一樣,很納悶细办。橙凳。。)

2.4 動態(tài)對象年齡判定

為了能更好的適應不同程序的內(nèi)存情況笑撞,虛擬機并不是永遠要求對象的年齡必須達到MaxTenuringThreshold參數(shù)的值時才能晉升到老年代岛啸。如果在Survivor中所有相同年齡的對象大小的總和超過Survivor空間的一半,則年齡大于或等于該年齡對象就可以直接進入老年代茴肥,無需等到參數(shù)設定的年齡

/**
 * Created by cwj on 17-6-3.
 *
 * VM:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 *    -XX:MaxTenuringThreshold=15
 *    -XX:+PrintTenuringDistribution
 */
public class TestTenuringThreshold2 {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] allocation1,allocation2,allocation3,allocation4;

        allocation1 = new byte[_1MB / 4];
        allocation2 = new byte[_1MB / 4];

        allocation3 = new byte[4 * _1MB];
        allocation4 = new byte[4 * _1MB];
        //第一次GC

        allocation4 = null;
        allocation4 = new byte[4 * _1MB];
        //第二次GC
    }
}

執(zhí)行結(jié)果

可以看到結(jié)果中Survivor仍然是0坚踩,而老年代比預計的增加了6%,說明1,2并沒有等到15歲就直接進入了老年代瓤狐,因為這兩個對象加起來的和已經(jīng)達到了Survivor空間的一半瞬铸,并且他們是同年的。我們只要注釋掉一個础锐,另外一個就不會進入老年代了嗓节。

2.5 空間分配擔保

新生代不夠放了就往老年代里面挪,那老年代也有不夠的時候啊皆警,這時候怎么辦呢拦宣?如果老年代的連續(xù)空間大于新生代對象的總大小,或者歷次晉升的平均大小信姓,就可以進行安全的MinorGC恢着,如果不大于那么就會執(zhí)行一次FullGC來釋放老年代的空間。

/**
 * Created by cwj on 17-6-3.
 *
 * VM:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 *    -XX:+PrintTenuringDistribution
 */
public class TestTenuringThreshold3 {
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] allocation1,allocation2,allocation3,allocation4,allocation5,allocation6,allocation7;

        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation1 = null;

        allocation4 = new byte[2 * _1MB];
        allocation5 = new byte[2 * _1MB];
        allocation6 = new byte[2 * _1MB];

        allocation4 = null;
        allocation5 = null;
        allocation6 = null;
        allocation7 = new byte[2 * _1MB];
    }
}

執(zhí)行結(jié)果
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末财破,一起剝皮案震驚了整個濱河市掰派,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌左痢,老刑警劉巖靡羡,帶你破解...
    沈念sama閱讀 211,496評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件系洛,死亡現(xiàn)場離奇詭異,居然都是意外死亡略步,警方通過查閱死者的電腦和手機描扯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趟薄,“玉大人绽诚,你說我怎么就攤上這事『技澹” “怎么了恩够?”我有些...
    開封第一講書人閱讀 157,091評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長羡铲。 經(jīng)常有香客問我蜂桶,道長,這世上最難降的妖魔是什么也切? 我笑而不...
    開封第一講書人閱讀 56,458評論 1 283
  • 正文 為了忘掉前任扑媚,我火速辦了婚禮,結(jié)果婚禮上雷恃,老公的妹妹穿的比我還像新娘疆股。我一直安慰自己,他們只是感情好倒槐,可當我...
    茶點故事閱讀 65,542評論 6 385
  • 文/花漫 我一把揭開白布押桃。 她就那樣靜靜地躺著,像睡著了一般导犹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羡忘,一...
    開封第一講書人閱讀 49,802評論 1 290
  • 那天谎痢,我揣著相機與錄音,去河邊找鬼卷雕。 笑死节猿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的漫雕。 我是一名探鬼主播滨嘱,決...
    沈念sama閱讀 38,945評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浸间!你這毒婦竟也來了太雨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,709評論 0 266
  • 序言:老撾萬榮一對情侶失蹤魁蒜,失蹤者是張志新(化名)和其女友劉穎囊扳,沒想到半個月后吩翻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,158評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡锥咸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,502評論 2 327
  • 正文 我和宋清朗相戀三年狭瞎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏予。...
    茶點故事閱讀 38,637評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡泣棋,死狀恐怖午乓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤婶恼,帶...
    沈念sama閱讀 34,300評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站衣厘,受9級特大地震影響树酪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸟廓,卻給世界環(huán)境...
    茶點故事閱讀 39,911評論 3 313
  • 文/蒙蒙 一从祝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧引谜,春花似錦牍陌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,744評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贝室,卻和暖如春契讲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滑频。 一陣腳步聲響...
    開封第一講書人閱讀 31,982評論 1 266
  • 我被黑心中介騙來泰國打工捡偏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人峡迷。 一個月前我還...
    沈念sama閱讀 46,344評論 2 360
  • 正文 我出身青樓银伟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绘搞。 傳聞我的和親對象是個殘疾皇子彤避,可洞房花燭夜當晚...
    茶點故事閱讀 43,500評論 2 348

推薦閱讀更多精彩內(nèi)容