本文思路
1.JVM運(yùn)行時(shí)內(nèi)存劃分
2.對(duì)象創(chuàng)建內(nèi)存動(dòng)作
3.Java線程棧
4.JVM垃圾收集器
1.JVM運(yùn)行時(shí)內(nèi)存劃分

1.1 程序計(jì)數(shù)器
當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。字節(jié)碼解釋器工作的時(shí)候就是通過改變這個(gè)計(jì)數(shù)值來選取下一條要執(zhí)行的字節(jié)碼指令。
1.2 虛擬機(jī)棧(java方法棧)
線程私有,java方法執(zhí)行時(shí)的內(nèi)存模型,每個(gè)方法執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量表、操作數(shù)棧、方法出口等信息
局部變量表其實(shí)就是一個(gè)方法內(nèi)的局部變量對(duì)應(yīng)的指針。
1.3 本地方法棧
Native 方法執(zhí)行的線程棧
1.4 方法區(qū)
線程共享區(qū)域,存儲(chǔ)已經(jīng)被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,即時(shí)編譯后的代碼等數(shù)據(jù)。
運(yùn)行時(shí)常量池
方法區(qū)的一塊,保存Class文件中描述的符號(hào)引用,也會(huì)把翻譯出來的直接引用也存儲(chǔ)在這里。它的主要特征是具備動(dòng)態(tài)性,編譯器和運(yùn)行時(shí)的常量都可以進(jìn)入,比如的string 字符串常量池 intern()方法,就是利用的該特性。
1.5 堆
JVM里所管理的內(nèi)存中最大的一塊,線程共享,存放所有對(duì)象實(shí)例。GC也是針對(duì)該區(qū)進(jìn)行回收。
2.對(duì)象創(chuàng)建內(nèi)存動(dòng)作
我們new一個(gè)對(duì)象的時(shí)候,都發(fā)生了什么?
2.1 jvm看到new指令,判斷指令參數(shù)是否能在常量池中定位到這個(gè)類的符號(hào)引用(可以認(rèn)為我們的類名),檢查這個(gè)符號(hào)引用代表的類是否已被加載、解析和初始化過,沒有的話需要進(jìn)行class加載。
2.2 jvm從堆里分配內(nèi)存
2.3 分配的內(nèi)存空間都初始化為零,保證對(duì)象的實(shí)例字段再Java代碼中不賦初始值就能直接使用。
2.4 jvm對(duì)對(duì)象做一些設(shè)置。設(shè)置存放在對(duì)象頭,對(duì)象頭里主要包含一些hashcode、GC、元數(shù)據(jù)、鎖等。
2.5 執(zhí)行init方法。此刻對(duì)于JVM一個(gè)對(duì)象已經(jīng)創(chuàng)建完畢,接下來會(huì)按照程序員的一樣做一些初始化動(dòng)作。

3.Java線程棧
每執(zhí)行一個(gè)方法,jvm都會(huì)為此創(chuàng)建一個(gè)棧幀,我們的一條調(diào)用鏈路,有一個(gè)一個(gè)的棧幀組成,最后形成一個(gè)線程棧,當(dāng)棧的長度過長或者超過內(nèi)存大小就好報(bào)棧溢出的異常。
基本數(shù)據(jù)類型,string這些的指針指向的是常量池。而實(shí)例對(duì)象也就統(tǒng)稱為reference類型,指向的是一個(gè)對(duì)象的引用,訪問堆中的對(duì)象具體位置有兩種方式,通過句柄池進(jìn)行句柄訪問,另一種直接通過指針。
3.1 句柄方式
java堆中會(huì)劃分一塊內(nèi)存用來作為句柄池,reference存儲(chǔ)的就是句柄地址,而句柄包含了實(shí)例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址。具有穩(wěn)定的句柄地址的優(yōu)勢(shì),當(dāng)對(duì)象移動(dòng),只會(huì)更改句柄中的實(shí)例數(shù)據(jù)指針。

3.2 直接指針訪問
reference存儲(chǔ)的直接就是對(duì)象地址,速度快,節(jié)省了一次指針定位的開銷。java中對(duì)象的訪問特別多,所以在java中該方式使用的最多。

4.JVM垃圾收集器
棧,程序計(jì)數(shù)器這些隨線程而生,隨線程而忘。而Java堆和方法區(qū)則是在運(yùn)行期間才知道會(huì)創(chuàng)建哪些對(duì)象,需要多少內(nèi)存。垃圾回收器所關(guān)注的則是這部分內(nèi)存。
如何判斷對(duì)象對(duì)象已死?
目前主流實(shí)現(xiàn)主要基于可達(dá)性分析來判定對(duì)象存活。大概思路就是通過GC ROOT 對(duì)象向下分析,不在鏈路上的對(duì)象則認(rèn)為是已死對(duì)象。

哪些對(duì)象可以作為GC ROOT對(duì)象?
- 虛擬機(jī)棧中引用的對(duì)象
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)常量引用的對(duì)象
- 本地方法棧中引用的對(duì)象
安全區(qū)域
JVM可以通過GC ROOT快速定位到要回收的對(duì)象,但是程序是在運(yùn)行中的,對(duì)象的關(guān)系也會(huì)發(fā)生變化。JVM只有當(dāng)程序進(jìn)入安全點(diǎn)時(shí),然后暫停程序進(jìn)行回收。
安全點(diǎn)的選定是以程序是否具有長時(shí)間執(zhí)行的特征為標(biāo)準(zhǔn)進(jìn)行選定。“長時(shí)間執(zhí)行”最明顯的特征就是指令序列復(fù)用(一組),如方法跳轉(zhuǎn)、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等,具備這些功能的指令才會(huì)產(chǎn)生safePoint。
在GC發(fā)生時(shí),一般會(huì)采用主動(dòng)式中斷,jvm在要發(fā)生GC時(shí)不會(huì)干預(yù)線程,會(huì)設(shè)置一個(gè)標(biāo)志,所有的線程主動(dòng)去輪詢,當(dāng)執(zhí)行線程發(fā)現(xiàn)了這個(gè)標(biāo)識(shí)就自己中斷掛起。
回收算法
標(biāo)記清除
步驟:將內(nèi)存中需要回收的對(duì)象打上標(biāo)記,然后回收打上標(biāo)記的對(duì)象。
缺點(diǎn):會(huì)產(chǎn)生內(nèi)存碎片
復(fù)制
步驟:將內(nèi)存一分為2,需要回收的時(shí)候,將存活對(duì)象copy到另一個(gè)內(nèi)存區(qū)域,然后原來的內(nèi)存區(qū)域就只剩下待回收的對(duì)象,直接全部清除
缺點(diǎn):可使用內(nèi)存只有一半,代價(jià)太大
標(biāo)記整理
和標(biāo)記清除類似,多了一個(gè)整理的過程,在清除后,會(huì)讓存活的對(duì)象向一個(gè)方向移動(dòng),最后達(dá)到內(nèi)存連續(xù),解決碎片問題。
收集器
JVM采用分代垃圾回收。在JVM的內(nèi)存空間中把堆空間分為年老代和年輕代。將大量(據(jù)說是90%以上)創(chuàng)建了沒多久就會(huì)消亡的對(duì)象存儲(chǔ)在年輕代,而年老代中存放生命周期長久的實(shí)例對(duì)象。年輕代中又被分為Eden區(qū)(圣經(jīng)中的伊甸園)、和兩個(gè)Survivor區(qū)。新的對(duì)象分配是首先放在Eden區(qū),Survivor區(qū)作為Eden區(qū)和Old區(qū)的緩沖,在Survivor區(qū)的對(duì)象經(jīng)歷若干次收集仍然存活的,就會(huì)被轉(zhuǎn)移到年老區(qū)。

JVM提供了很多個(gè)收集器,目前主要有Serial,Serial Old,ParNew, Paralle Scavenge,Paralle Scavenge Old,CMS,G1。當(dāng)前用的最多的是 CMS和G1。
Serial 和 Serial Old
單線程,簡單高效。進(jìn)行GC的時(shí)候會(huì)把所有工作線程停掉,是早期的回收期。serial old是專門回收老年代的。
ParNew
Serial的并發(fā)版本
Paralle Scavenge 和 Paralle Scavenge Old
采用復(fù)制算法,追求吞吐量,可以高效的利用CPU時(shí)間,盡快完成程序的運(yùn)算任務(wù),適合在后臺(tái)運(yùn)算而不需要太多交互的程序。
CMS
是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。對(duì)于尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,以給用戶帶來較好的體驗(yàn)的系統(tǒng)很適合。
采用標(biāo)記清除算法,所以缺點(diǎn)就是會(huì)有大量的空間碎片。
G1
暫時(shí)看的不是特別清楚,可以參考下
https://www.cnblogs.com/ASPNET2008/p/6496481.html
https://mp.weixin.qq.com/s/ZwlT89vsvD2e0qEuxZto3Q
自行理解。
--
參考文章
http://www.idouba.net/a-simple-example-demo-jvm-allocation-and-gc/
http://gityuan.com/2016/01/09/java-memory/
https://blog.csdn.net/top_code/article/details/51288529