JVM 垃圾收集器與內(nèi)存分配策略

JVM 垃圾收集器與內(nèi)存分配策略

JVM內(nèi)存區(qū)域可知Java運(yùn)行時內(nèi)存的各個區(qū)域喘批。其中程序計數(shù)器研儒、虛擬機(jī)棧换途、本地方法棧3個區(qū)域隨線程而生棒仍,隨線程而滅缨历,當(dāng)方法結(jié)束或者線程結(jié)束時缔御,內(nèi)存就會跟著被回收了朝氓。

而只有處于運(yùn)行期間径荔,我們才能知道程序究竟會創(chuàng)建哪些對象垂寥,創(chuàng)建多少個對象颠黎,所以Java堆方法區(qū)這兩個區(qū)域內(nèi)存的分配和回收時動態(tài)的,垃圾收集器也只關(guān)注這部分內(nèi)存的管理滞项。


一.對象存活判斷

1.引用計數(shù)算法

在對象中添加一個引用計數(shù)器狭归,每當(dāng)有一個地方引用它時,計數(shù)器值就加一文判;當(dāng)引用失效時过椎,計數(shù)器值就減一;任何時候計數(shù)器值為零的對象就是不可能再被使用的戏仓。

  • 優(yōu)點:實現(xiàn)簡單疚宇,效率高
  • 缺點:無法解決對象相互循環(huán)引用的問題——會導(dǎo)致對象的引用雖然存在,但是已經(jīng)不可能再被使用赏殃,卻無法被回收敷待。

2.可達(dá)性分析算法

通過一系列的稱為”GC Roots”的對象作為起始點, 從這些節(jié)點開始向下搜索, 搜索走過的路徑稱為引用鏈(Reference Chain), 當(dāng)一個對象到GC Roots不可達(dá)(也就是不存在引用鏈)的時候, 證明對象是不可用的。如下圖: Object5仁热、6榜揖、7 雖然互有關(guān)聯(lián), 但它們到GC Roots是不可達(dá)的, 因此也會被判定為可回收的對象。

image.png

在Java, 可作為GC Roots的對象包括:

  • 在虛擬機(jī)棧中引用的對象
  • 在方法區(qū)中類靜態(tài)屬性引用的對象
  • 在方法區(qū)中常量引用的對象
  • 在本地方法棧中JNI(Native方法)引用的對象
  • Java虛擬機(jī)內(nèi)部的引用
  • 所有被同步鎖(synchronized關(guān)鍵字)持有的對象

二.如何回收(垃圾收集算法)

分代收集理論:

  1. 弱分代假說:絕大多數(shù)對象都是朝生夕滅的抗蠢。
  2. 強(qiáng)分代假說:熬過多次垃圾收集的對象就越難以消亡根盒。

Java堆劃分為新生代和老生代兩個區(qū)域。在新生代中物蝙,每次垃圾收集時都發(fā)現(xiàn)有大批對象死去炎滞,而每次回收后存活的少量對象,將會逐步晉升到老年代中存放诬乞。

1.標(biāo)記-清除算法

分為標(biāo)記和清除兩個階段先標(biāo)記出需要回收的對象(可達(dá)性分析算法或者引用計數(shù)算法)册赛,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象钠导。

缺點:

  • 效率問題,標(biāo)記和清除效率都不高森瘪。
  • 空間問題牡属,標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后在程序運(yùn)行過程中分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾回收動作扼睬。


    image.png

2.標(biāo)記-復(fù)制算法

將可用內(nèi)存劃分為大小相等的兩塊逮栅,每次只使用其中的一塊。當(dāng)這塊用完了窗宇,就將還存活的復(fù)制到另一塊上措伐,然后將已使用過的另一半內(nèi)存一次性清除。

新生代中的對象98%都是朝生夕死的军俊。將內(nèi)存分為較大Eden和兩個較小的survivor空間侥加。每次使用Eden和其中一個survivor,回收時將存活的對象一次性地復(fù)制到另一塊survivor中粪躬,再清理掉Eden和已用過的survivor担败。

HotSpot虛擬機(jī)Eden與Survivor默認(rèn)的大小比例為8:1:1。即只讓10%的新生代被浪費的镰官,survivor空間不夠時提前,需要依賴其他內(nèi)存(老年代)進(jìn)行分配擔(dān)保,即讓對象進(jìn)入老年代泳唠。

優(yōu)點:

  • 對整個半?yún)^(qū)進(jìn)行回收狈网,不會出現(xiàn)空間碎片。
  • 如果內(nèi)存中多數(shù)對象都是可回收警检,就只需復(fù)制少數(shù)的存活對象。

缺點:

  • 如果內(nèi)存中多數(shù)對象都是存活的害淤,將產(chǎn)生大量的內(nèi)存間復(fù)制的開銷扇雕。
  • 可用內(nèi)存縮減了一半,造成空間浪費窥摄。
image.png

3.標(biāo)記-整理算法

復(fù)制在對象存活率較高時效率很低镶奉。根據(jù)老年代的特點提出該算法。標(biāo)記過程同標(biāo)記清除一樣崭放,但不是直接對可回收對象進(jìn)行清理哨苛,而是讓存活對象朝著一端移動,然后直接清理掉邊界以外的內(nèi)存币砂。

標(biāo)記-整理與標(biāo)記-清除的差異在于整理是移動式的回收算法建峭,清除是非移動的。

image.png

根據(jù)各年代特點分別采用最適當(dāng)?shù)腉C算法决摧。

在新生代中每次垃圾收集都能發(fā)現(xiàn)大批對象已死, 只有少量存活亿蒸,因此選用標(biāo)記-復(fù)制算法, 只需要復(fù)制少量存活對象就可以完成收集凑兰。

在老年代因為對象存活率高、沒有額外空間對它進(jìn)行分配擔(dān)保, 就必須采用標(biāo)記—清理標(biāo)記—整理算法來進(jìn)行回收, 不必進(jìn)行內(nèi)存復(fù)制, 且直接騰出空閑內(nèi)存边锁。即:

  • 新生代:存活率低姑食,使用復(fù)制算法
  • 老年代:存活率高,使用“標(biāo)記-整理”或“標(biāo)記-清除”算法

三.垃圾收集器

  • 新生代:Serial收集器  ParNew收集器  Parallel Scavenge收集器
  • 老年代:Serial Old收集器  Parallel Old收集器  CMS收集器
image.png

新生代:

1.Serial收集器

標(biāo)記復(fù)制茅坛。單線程收集器音半,只會使用一個處理器或一條收集線程進(jìn)行垃圾收集,而且在垃圾收集時贡蓖,必須暫停其他所有工作線程曹鸠,直到收集結(jié)束。優(yōu)點:簡單高效摩梧。

image.png

2.ParNew收集器

標(biāo)記復(fù)制物延。是Serial收集器的多線程并行版本,同時使用多條線程進(jìn)行垃圾收集仅父,其余與Serial一樣叛薯。目前唯一能與CMS收集器配合工作。

image.png

3.Parallel Scavenge收集器

標(biāo)記復(fù)制笙纤。并行收集的多線程收集器耗溜。CMS等收集器的關(guān)注點是盡可能地縮短垃圾收集時用戶線程的停頓時間,而PS收集器的目的則是達(dá)到一個可控制的吞吐量省容。吞吐量即CPU用于運(yùn)行用戶代碼的時間與CPU總消耗時間的比值(吞吐量=運(yùn)行用戶代碼的時間/(運(yùn)行用戶代碼的時間+垃圾收集的時間))抖拴。

image.png

老年代:

4.Serial Old收集器

標(biāo)記整理。Serial 收集器的老年代版本腥椒,單線程收集器阿宅。

5.Parallel Old收集器

標(biāo)記整理。Parallel Old是Parallel Scavenge的老年代版本笼蛛,多線程收集器洒放。

6.CMS收集器(Concurrent Mark Sweep并發(fā)標(biāo)記清除)

標(biāo)記清除。

并發(fā)收集滨砍,回收停頓時間短往湿。

步驟:

  1. 初始標(biāo)記:停掉用戶其他線程,僅標(biāo)記GCRoots能直接關(guān)聯(lián)到的對象惋戏,速度很快领追。
  2. 并發(fā)標(biāo)記:從GCRoots的直接關(guān)聯(lián)對象開始遍歷整個對象圖的過程,耗時長但不需要停頓用戶線程响逢,與垃圾收集線程一起并發(fā)執(zhí)行绒窑。
  3. 重新標(biāo)記:修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄。耗時比初始標(biāo)記長一點舔亭,但遠(yuǎn)低于并發(fā)標(biāo)記回论。
  4. 并發(fā)清除:清理刪除掉那些標(biāo)記階段判斷為死亡的對象散罕,因為標(biāo)記清楚算法不需要移動存活對象,所以這個階段也是可以與用戶線程同時并發(fā)的傀蓉。

總體而言欧漱,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。

image.png

缺點:

  • CMS對處理器資源非常敏感葬燎。
  • CMS無法處理浮動垃圾(Floating Garbage)误甚,可能出現(xiàn)Concurrent Mode Failure失敗而導(dǎo)致另一次Full GC的產(chǎn)生。
  • CMS是標(biāo)記清除谱净,會產(chǎn)生大量碎片空間窑邦,對大對象內(nèi)存分配帶來麻煩。

7.G1收集器(Garbage First)

與其他收集器不同壕探,G1把連續(xù)的Java堆劃分為多個大小相等的獨立區(qū)域(Region)冈钦,每一個Region都可以根據(jù)需要扮演新勝達(dá)的Eden空間、Survivor空間李请,或者老年代空間瞧筛。收集器對不同角色的Region采用不同的策略去處理。優(yōu)先處理回收價值收益最大的那些Region导盅,也就是Garbage First的由來较幌。

  • 從整體來看:“標(biāo)記-整理” 算法
  • 從局部(兩個Region之間)來看:“復(fù)制”算法

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

堆內(nèi)存劃分為 Eden、Survivor 和 Tenured/Old 空間白翻,如下圖所示:

image.png

從年輕代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱為 Minor GC乍炉,對老年代GC稱為Major GC,而Full GC是對整個堆來說的。

  • 新生代GC(Minor GC):發(fā)生在新生代的垃圾收集動作滤馍,非常頻繁岛琼,一般回收速度也比較快。
  • 老年代GC(Major GC/Full GC):發(fā)生在老年代的垃圾收集動作巢株,一般會伴隨Minor GC 速度一般比Minor GC慢上10倍以上槐瑞。

對象的內(nèi)存分配從大方向講,就是在堆上分配纯续,對象主要分配在新生代的Eden區(qū)上随珠,如果啟動了本地線程分配緩沖灭袁,將按線程優(yōu)先在TLAB上分配猬错,少數(shù)情況下也有可能直接分配在老年代中,分配的規(guī)則并非百分之百固定的茸歧,細(xì)節(jié)取決于當(dāng)前使用的是哪一種垃圾收集器的組合倦炒,還有虛擬機(jī)中與內(nèi)存相關(guān)的參數(shù)設(shè)置。

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

大多數(shù)情況下软瞎,對象在新生代的Eden區(qū)分配逢唤,當(dāng)Eden區(qū)沒有足夠空間時拉讯,虛擬機(jī)將發(fā)起一次Minor GC。

2.大對象直接進(jìn)入老年代

所謂大對象是指需要大量連續(xù)內(nèi)存空間的對象鳖藕,最典型的大對象就是那種很長的字符串以及數(shù)組魔慷。大對象對虛擬機(jī)的內(nèi)存分配來說就是一個壞消息,經(jīng)常出現(xiàn)大對象容易導(dǎo)致內(nèi)存還有不少空間時就提前觸發(fā)GC以獲取足夠的連續(xù)空間來“安置”它們著恩。

3.長期存活的對象將進(jìn)入老年代

虛擬機(jī)給每個對象定義了一個對象年齡(Age)計數(shù)器院尔。如果對象在Eden出生并經(jīng)過第一次Minor GC后仍然存活,并且能夠被Survivor容納喉誊,將被移動到Survivor空間中邀摆,并且對象年齡為1.對象在Survivor區(qū)每“熬過”一次Minor GC,年齡就增加一歲伍茄,當(dāng)年齡增加到一定程度(默認(rèn)為15歲)栋盹,就將被晉升到老年代中。對象晉升老年代年齡的閾值敷矫,可以通過參數(shù)設(shè)置例获。

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

如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或者等于該年齡的對象就可以直接進(jìn)入老年代沪饺,無需等到要求的年齡

5.空間分配擔(dān)保

發(fā)生Minor GC之前躏敢,虛擬機(jī)會檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象的總空間,如果成立整葡,那么Minor GC確保是安全的件余,如果不成立,則虛擬機(jī)會查看HandlePromotionFailure設(shè)置值是否允許擔(dān)保失敗遭居。如果允許啼器,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于俱萍,將嘗試進(jìn)行一次Minor GC端壳,盡管是有風(fēng)險的,如果小于或者設(shè)置不允許冒險枪蘑,那這時也要進(jìn)行一次Full GC损谦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岳颇,隨后出現(xiàn)的幾起案子照捡,更是在濱河造成了極大的恐慌,老刑警劉巖话侧,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栗精,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悲立,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門鹿寨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人薪夕,你說我怎么就攤上這事脚草。” “怎么了原献?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵玩讳,是天一觀的道長。 經(jīng)常有香客問我嚼贡,道長熏纯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任粤策,我火速辦了婚禮樟澜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叮盘。我一直安慰自己秩贰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布柔吼。 她就那樣靜靜地躺著毒费,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愈魏。 梳的紋絲不亂的頭發(fā)上觅玻,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音培漏,去河邊找鬼溪厘。 笑死,一個胖子當(dāng)著我的面吹牛牌柄,可吹牛的內(nèi)容都是我干的畸悬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼珊佣,長吁一口氣:“原來是場噩夢啊……” “哼蹋宦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咒锻,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤冷冗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虫碉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贾惦,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡胸梆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年敦捧,在試婚紗的時候發(fā)現(xiàn)自己被綠了须板。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡兢卵,死狀恐怖习瑰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秽荤,我是刑警寧澤甜奄,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窃款,受9級特大地震影響课兄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜晨继,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一烟阐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧紊扬,春花似錦蜒茄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腹缩,卻和暖如春屿聋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藏鹊。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工胜臊, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伙判。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓象对,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宴抚。 傳聞我的和親對象是個殘疾皇子勒魔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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