[016]JVM如何分配內(nèi)存及其回收策略01

背景

以前看了周志明的《深入理解Java虛擬機(jī)》,今天突然想談起它來(lái)爸吮。
所以今天這篇文章會(huì)說(shuō)說(shuō) java的內(nèi)存分配策略和垃圾收集器囤采。

提出問(wèn)題

java 中內(nèi)存如何申請(qǐng)的,什么樣的數(shù)據(jù)存放在新生代吗坚,什么樣的數(shù)據(jù)存放在老生代,什么樣的數(shù)據(jù)存放在永久代呆万。

數(shù)據(jù)的回收:這3個(gè)區(qū)域的數(shù)據(jù)是如何回收的呢商源?各使用什么樣的垃圾收集器。
今天的文章圍繞他們解決谋减。

對(duì)象如何被轉(zhuǎn)移

這里有兩個(gè)概念:MinGc:新生代GC, Major GC:老生代GC牡彻。
新生代往老生代轉(zhuǎn)移的原則是:
1).在進(jìn)行15次 MinGC還存活下來(lái)的就轉(zhuǎn)移到 Old GC。
2).如果對(duì)象太大,直接放到 Old區(qū)庄吼。
3).如果MinGC 過(guò)程中缎除,Survivor放不下會(huì)被擔(dān)保到Old區(qū)。

當(dāng)Eden區(qū)滿了就會(huì)觸發(fā)MinGC总寻,當(dāng)Old區(qū)滿了就會(huì)觸發(fā)Major GC器罐。
為什么會(huì)有2個(gè)Survivor,是由于必須經(jīng)過(guò)15次MinGC才會(huì)轉(zhuǎn)移到Old GC渐行,起到轉(zhuǎn)騰的作用轰坊。
比如 第一次MinGC:Eden +Suv1 -> Surv2,第二次Min GC: Eden + Surv2 -> surv1。
這里虛擬機(jī)為每個(gè)對(duì)象定義了年齡計(jì)數(shù)器祟印,當(dāng)年齡大于閥值得時(shí)候就進(jìn)入老年代肴沫。

大對(duì)象直接進(jìn)入老年代,大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)蕴忆。這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝(新生代采用復(fù)制算法收集內(nèi)存)颤芬。

堆內(nèi)存如何被申請(qǐng)

虛擬機(jī)如何執(zhí)行 Class1 c1 = new Class1()這條指令。
在執(zhí)行這條指令的時(shí)候要做兩件事情:
1.檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)符號(hào)引用孽文,并且檢查這個(gè)符號(hào)引用是否被加載驻襟、解析、初始化芋哭。如果沒有必須先執(zhí)行類加載過(guò)程沉衣。
2.申請(qǐng)內(nèi)存,內(nèi)容申請(qǐng)需要看堆中內(nèi)存是否連續(xù)减牺,如果是連續(xù)通過(guò)移動(dòng)指針頭(稱為指針膨脹)豌习。如果不連續(xù)的話就通過(guò)“空閑列表”定位。 而內(nèi)存是否連續(xù)跟垃圾回收算法是否帶有壓縮功能有關(guān)拔疚。

serial和parNew帶有壓縮功能肥隆,而CMS基于mark-sweep算法的收集器,通常采用空閑列表稚失。
分配空間的過(guò)程中栋艳,還需要考慮一個(gè)問(wèn)題是。如果多個(gè)線程同時(shí)修改指針會(huì)出現(xiàn)并發(fā)的問(wèn)題句各。
虛擬機(jī)采用兩種辦法 a).CAS+失敗重試(CAS就是我們所說(shuō)的樂觀鎖機(jī)制)吸占。 b).內(nèi)存分配的動(dòng)作劃分到不同的線程空間中。
3.內(nèi)存申請(qǐng)完之后就是初始化凿宾。
這里涉及到對(duì)象的內(nèi)存布局矾屯,對(duì)象分為對(duì)象頭(如hashcode,對(duì)象類型指針)、實(shí)例數(shù)據(jù)初厚、對(duì)象填充件蚕。

對(duì)象如何被定位呢?
我們知道對(duì)象的引用放在棧里,所以我們有兩種方式引用到堆里的內(nèi)存排作。
1.棧里的reference 直接指向 堆里的對(duì)象牵啦。
2.棧里reference 指向堆里 對(duì)象句柄(對(duì)象句柄這個(gè)地址是固定的),對(duì)象類型數(shù)據(jù)指針是放在 對(duì)象句柄中的纽绍,所以當(dāng)對(duì)象移動(dòng)了只會(huì)修改 對(duì)象句柄中實(shí)例數(shù)據(jù)指針蕾久。

由于使用句柄的方式多了一次指針定位的開銷势似,所以比第一種會(huì)慢一些拌夏。

什么樣的數(shù)據(jù)會(huì)被回收?

簡(jiǎn)單的說(shuō)就是無(wú)用的數(shù)據(jù)應(yīng)該被回收履因,什么是無(wú)用的呢障簿?
怎么判定無(wú)用:市面上有
1).引用計(jì)數(shù)法(解決不了循環(huán)引用的場(chǎng)景 A與B互相引用)
2).可達(dá)性分析算法
這個(gè)算法就是通過(guò) 一系列稱為 "GC Roots"的對(duì)象作為起點(diǎn),搜索鎖走過(guò)的路徑稱為引用鏈栅迄。當(dāng)一個(gè)對(duì)象到GC Roots沒有任可達(dá)路徑則會(huì)被回收站故。

那什么是GC Roots對(duì)象呢?
GC Roots對(duì)象包含如下幾點(diǎn):a.虛擬機(jī)棧中引用的對(duì)象毅舆。 b.方法去中類靜態(tài)屬性引用的對(duì)象西篓。 c.方法區(qū)中常量引用的對(duì)象。 d.本地方法棧中JNI引用的對(duì)象憋活。

什么時(shí)候被回收

什么時(shí)候被回收是根據(jù)其引用的類型來(lái)決定的岂津,java中的引用分為以下幾種。
1.強(qiáng)引用 2.軟引用 3.弱引用 3.虛引用
強(qiáng)引用就是類似于 Class1 c1 = new Class1(); 永遠(yuǎn)不會(huì)被回收
軟引用用來(lái)描述有用但是非必須的悦即,在內(nèi)存溢出的會(huì)被回收吮成。 java中通過(guò)SoftReference實(shí)現(xiàn)軟引用。
弱引用強(qiáng)度比軟引用更加的弱一些辜梳,再下一次垃圾回收時(shí)就會(huì)被回收粱甫。
虛引用,我們不會(huì)通過(guò)虛引用來(lái)獲取一個(gè)對(duì)象也不會(huì)對(duì)生存時(shí)間構(gòu)成影響作瞄,它的作用就是在被回收掉后收到一個(gè)系統(tǒng)通知茶宵。

怎么被回收

垃圾回收算法

這里描述使用什么算法及其如何回收內(nèi)存。
垃圾回收算法應(yīng)該考慮以下幾點(diǎn):
1).回收的效率和時(shí)間宗挥。
2).垃圾回收的時(shí)候是否影響正在運(yùn)行的程序乌庶。

我們先不考慮分代的問(wèn)題,垃圾回收就是想把沒有用的清楚掉把有用的留下來(lái)属韧。對(duì)于無(wú)用的對(duì)象安拟,我們可以標(biāo)記清楚 - 標(biāo)記清楚法。 對(duì)于有用的對(duì)象我們可以采用標(biāo)記整理法 或者 復(fù)制法宵喂。

這里提一下復(fù)制法:新生代中包含(Eden + Survivor1 + Survivor2)糠赦,每次會(huì)把Eden +Survivor中的存活對(duì)象移動(dòng)到另一個(gè) Survivor中,如果另一個(gè)Survivor空間不夠就需要老生代擔(dān)保。

而實(shí)際jvm中是分為新生代拙泽、老生代淌山。對(duì)于新生代由于回收頻率高存活對(duì)象比較少,所以采用復(fù)制法顾瞻。對(duì)于老生代由于對(duì)象存活率高沒有額外的區(qū)域進(jìn)行擔(dān)保所以使用(標(biāo)記清除泼疑、標(biāo)記整理)法。

垃圾回收器

前面談到了收集算法荷荤,但是垃圾具體是通過(guò)收集器進(jìn)行回收的退渗。市面上最簡(jiǎn)單的垃圾收集器是serial收集器,由于其簡(jiǎn)單專心做垃圾收集適用于運(yùn)行在client模式下的虛擬機(jī)(新生代也只有幾十到100多兆)蕴纳。

a).parNew是Serial的多線程版本会油,其余行為包括垃圾收集算法(復(fù)制算法)都與Serial收集器一致。parNew是運(yùn)行在server下的首選新生代收集器古毛。

b).Cocurrent Mark-sweep翻翩,這個(gè)是虛擬機(jī)真正意義上第一款并發(fā)收集器,由于其使用Mark-sweep算法所以其適用于老生代稻薇。

c).Parallel Scavenge 提供兩個(gè)參數(shù)MaxGCPauseMillis,GCTimeRatio來(lái)設(shè)置虛擬機(jī)優(yōu)化目標(biāo)嫂冻,該收集器注重的的是吞吐量,對(duì)于與用戶交互少但是有很多后臺(tái)程序比較適合塞椎。 -- 老生代還是新生代桨仿??忱屑?

d).Serial old 收集器是Serial的老年代版本(使用標(biāo)記-整理算法)蹬敲, Parralle Scavenge無(wú)法與CMS工作只能與Serial Old配合一起工作。這是parralle old收集器出現(xiàn)它采用的是標(biāo)記-整理 算法

d).CMS收集器莺戒,為什么Cocurrent特性呢伴嗡?仔細(xì)分析然來(lái)CMS收集器把收集過(guò)程分為4個(gè)子階段:
1).初始標(biāo)記 -- 把GC Root對(duì)象找出來(lái)
2).并發(fā)標(biāo)記 -- GC Roots tracing
3).重新標(biāo)記 -- 把進(jìn)行 并發(fā)標(biāo)記 那一段時(shí)間,標(biāo)記變動(dòng)的那一部分重新標(biāo)記从铲,這個(gè)階段比初始標(biāo)記時(shí)間長(zhǎng)比并發(fā)標(biāo)記時(shí)間短瘪校,但是必須停止用戶線程。
4).并發(fā)清除

初始標(biāo)記名段、重新標(biāo)記是需要”stop the world“阱扬,并發(fā)標(biāo)記和并發(fā)清除這兩個(gè)階段還是可以垃圾回收線程與用戶線程一起執(zhí)行。由于CMS是并發(fā)的執(zhí)行收集工作伸辟,所以它是一個(gè)很耗資源的收集器麻惶,會(huì)使得用戶程度整體執(zhí)行時(shí)間長(zhǎng)(雖然瞬時(shí)停頓感少了)。

由于CMS收集器與用戶線程一起工作信夫,所以不能像其他收集器比如par Old一樣等到老生代占到100%在啟用窃蹋,一般等到60%就啟用CMS收集器卡啰,這里有一個(gè)參數(shù)配置-XXCMSInitiatingOccupancyFraction。平常我們可以提高這個(gè)參數(shù)來(lái)降低CMS啟動(dòng)收集次數(shù)警没,但是如果比率過(guò)高匈辱,預(yù)留的空間不足用戶線程使用會(huì)出現(xiàn)“Cocurrent Mode Failure”,這樣的話性能反而會(huì)降低杀迹。

還有一個(gè)問(wèn)題CMS容易產(chǎn)生碎片亡脸,如果需要進(jìn)行碎片整理(無(wú)法并發(fā))的話必須停止用戶線程。我們通過(guò)一個(gè)參數(shù)CMSFullGCBeforeCompaction來(lái)控制執(zhí)行多少次無(wú)壓縮的收集然后執(zhí)行一次帶壓縮的收集树酪。

e).G1收集器
G1收集器在多核CPU等可以充分利用硬件環(huán)境浅碾,它是面向服務(wù)端的垃圾回收器。采用的是“標(biāo)記-整理”的方式嗅回,所以沒有碎片的出現(xiàn)及穗。
G1收集器的堆內(nèi)存與其他收集器有很大的區(qū)別,它將整個(gè)堆劃分為很多region绵载。G1能夠建立預(yù)測(cè)模型會(huì)跟蹤每個(gè)Region垃圾回收價(jià)值大小(回收所獲得的空間大小和及回收所需時(shí)間的經(jīng)驗(yàn)值)苛白,然后選一個(gè)價(jià)值最高的region進(jìn)行回收(這是Garbage-First 名稱的由來(lái))娃豹。

堆被分成許多region也會(huì)帶來(lái)一個(gè)問(wèn)題,如果一個(gè)region A被其他region B引用购裙,當(dāng)我回收A的時(shí)候我還要掃描整個(gè)堆懂版,把引用A的regin掃描出來(lái)。所以G1采用空間換時(shí)間的辦法即提供一個(gè)rememerSet的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)這些引用關(guān)系躏率。

如何查看垃圾收集器日志

開啟GC日志:參數(shù) - XX:+PrintGC(或者 - verbose:gc)開啟了簡(jiǎn)單 GC 日志模式躯畴,
簡(jiǎn)單GC日志不會(huì)打印具體使用的算法,比如下面日志:

[GC 246656K->243120K(376320K), 0.0929090 secs]
[Full GC 243120K->241951K(629760K), 1.5589690 secs]

第一行表示執(zhí)行的MinGC 堆空間使用從246656K 到 243120K 堆的總大小為376320K,執(zhí)行時(shí)間消耗0.0929090 secs薇芝。
第二行表示執(zhí)行了FUll GC堆空間從243120K 到 241951K蓬抄,執(zhí)行時(shí)間消耗1.5589690 secs。
這里沒有描述執(zhí)行GC的時(shí)間夯到,數(shù)據(jù)有沒有從新生代轉(zhuǎn)到老生代嚷缭,也沒有顯示具體使用的算法。

如果想要看詳細(xì)的GC日志耍贾,則-XX:PrintGCDetails,這時(shí)候會(huì)打印出詳細(xì)日志:
如下描述的是執(zhí)行了一次新生代GC阅爽,新生代使用由142816K減少到10752K。堆的總?cè)萘看笮∮?46648K減少到243136K荐开。

[GC
    [PSYoungGen: 142816K->10752K(142848K)] 246648K->243136K(375296K), 0.0935090 secs
]
[Times: user=0.55 sys=0.10, real=0.09 secs]

對(duì)于FullGC 詳細(xì)日志如下:
打印了每一代的堆使用情況變化付翁,其中PsYoungGen 9707 +ParOldGen 232244 = 堆的總使用大小 241951.

[Full GC
    [PSYoungGen: 10752K->9707K(142848K)]
    [ParOldGen: 232384K->232244K(485888K)] 243136K->241951K(628736K)
    [PSPermGen: 3162K->3161K(21504K)], 1.5265450 secs
]

如果想打印出時(shí)間參數(shù)可以使用- XX:+PrintGCTimeStamps 可以將時(shí)間和日期也加到 GC 日志中。
如果指定了 - XX:+PrintGCDateStamps晃听,每一行就添加上了絕對(duì)的日期和時(shí)間百侧。

2014-01-03T12:08:38.102-0100: [GC 66048K->53077K(251392K), 0.0959470 secs]
2014-01-03T12:08:38.239-0100: [GC 119125K->114661K(317440K), 0.1421720 secs]
2014-01-03T12:08:38.513-0100: [GC 246757K->243133K(375296K), 0.2761000 secs]

如果想輸出到文件 就加上 -Xloggc

寫完后的想法

對(duì)這塊知識(shí)着帽,邊看書邊收集資料整理理解。
下一篇文章將通過(guò)代碼具體模擬觸發(fā)MinGC移层,及其通過(guò)GC 日志分析仍翰,查看JVM活動(dòng)的細(xì)節(jié)。
這是我個(gè)人面對(duì)這個(gè)問(wèn)題的邏輯推導(dǎo)不是粘貼別人的答案花費(fèi)了我大半天的時(shí)間但是很值观话。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末予借,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子频蛔,更是在濱河造成了極大的恐慌灵迫,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晦溪,死亡現(xiàn)場(chǎng)離奇詭異瀑粥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)三圆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門狞换,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人舟肉,你說(shuō)我怎么就攤上這事黄选》朔玻” “怎么了屠缭?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵苗缩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我整慎,道長(zhǎng)脏款,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任裤园,我火速辦了婚禮撤师,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘比然。我一直安慰自己丈氓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布强法。 她就那樣靜靜地躺著万俗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饮怯。 梳的紋絲不亂的頭發(fā)上闰歪,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音蓖墅,去河邊找鬼库倘。 笑死临扮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的教翩。 我是一名探鬼主播杆勇,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼饱亿!你這毒婦竟也來(lái)了蚜退?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤彪笼,失蹤者是張志新(化名)和其女友劉穎钻注,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體配猫,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幅恋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泵肄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捆交。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凡伊,靈堂內(nèi)的尸體忽然破棺而出零渐,到底是詐尸還是另有隱情,我是刑警寧澤系忙,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站惠豺,受9級(jí)特大地震影響银还,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜洁墙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一蛹疯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧热监,春花似錦捺弦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至苦始,卻和暖如春寞钥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陌选。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工理郑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹄溉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓您炉,卻偏偏與公主長(zhǎng)得像柒爵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赚爵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

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