JVM-運(yùn)行時(shí)數(shù)據(jù)區(qū)域與對(duì)象創(chuàng)建步驟

Java虛擬機(jī)運(yùn)行時(shí)區(qū)域圖

程序計(jì)數(shù)器

是一塊較小的內(nèi)存空間,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器谓松。在虛擬機(jī)的概念模型里簸淀,字節(jié)碼解釋器工作時(shí)通過改變計(jì)數(shù)器的值來選取下一跳需要執(zhí)行的字節(jié)碼指令。

由于java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行實(shí)踐來實(shí)現(xiàn)的毒返,在任何一個(gè)確定的時(shí)刻租幕,一個(gè)處理器只能執(zhí)行一條線程的指令。為了線程切換后能恢復(fù)到正確的執(zhí)行位置拧簸,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器劲绪,讓各個(gè)線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ),因此程序計(jì)數(shù)器是“私有”的贾富。

如果線程執(zhí)行的是java方法歉眷,那計(jì)數(shù)器記錄的就是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果執(zhí)行的是native方法颤枪,計(jì)數(shù)器值為空(undefined)汗捡。此內(nèi)存區(qū)域是唯一一個(gè)不會(huì)出現(xiàn)OOM(OutOfMemoryError)情況的區(qū)域。

Java虛擬機(jī)棧

Java虛擬機(jī)棧也是“私有”的畏纲,生命周期與線程相同扇住。描述的是Java方法執(zhí)行的內(nèi)存模型,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀盗胀,用于存儲(chǔ)局部變量表艘蹋、操作數(shù)棧、動(dòng)態(tài)鏈接票灰、方法出口等信息女阀。一個(gè)方法從調(diào)用到執(zhí)行完成的過程,就對(duì)應(yīng)一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程屑迂。

局部變量表存放了編譯時(shí)期可知的各種基本數(shù)據(jù)類型浸策、對(duì)象引用(reference類型,不等同對(duì)象本身惹盼,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔樀拈唬部赡苁侵甘疽粋€(gè)代表對(duì)象的句柄或其他與此對(duì)象相關(guān)的位置)和returnAddress(指向了一條字節(jié)碼指令的地址)。局部變量表所需的內(nèi)存空間在編譯期間完成分配逻锐,在方法運(yùn)行期間不會(huì)改變局部變量表的大小夫晌。

本地方法棧

與虛擬機(jī)棧相似,但虛擬機(jī)棧執(zhí)行的是java方法昧诱,而本地方法棧執(zhí)行的是native方法晓淀。

Java堆

對(duì)于絕大多數(shù)應(yīng)用來說,堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊盏档,被所有線程所共享凶掰,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例蜈亩,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存懦窘。

堆是垃圾收集器管理的主要區(qū)域,從內(nèi)存回收的角度來看稚配,由于現(xiàn)在收集器基本采用分代收集算法畅涂,所以java堆還可以細(xì)分為:新生代和老年代,再往細(xì)里分可以分為:Eden空間道川、From Survivor空間午衰、To Survivor空間等(8:1:1)立宜;從內(nèi)存分配的角度來看,線程共享的堆可能劃分出多個(gè)線程私有的分配緩沖區(qū)(TLAB Thread Local Allocation Buffer)臊岸。進(jìn)一步劃分的目的是為了更好的回收內(nèi)存橙数,或者更快地分配內(nèi)存。

堆可以處于物理上不連續(xù)的內(nèi)存空間帅戒。在實(shí)現(xiàn)時(shí)灯帮,可以是固定大小,也可以是可擴(kuò)展(通過-Xmx和-Xms控制:最大和最小的堆內(nèi)存指定)逻住。

Java方法區(qū)

與堆一樣钟哥,是各個(gè)線程所共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類信息鄙信、常量瞪醋、靜態(tài)變量忿晕、即使編譯器編譯后的代碼等數(shù)據(jù)(可以看作是堆的一個(gè)邏輯部分)装诡。

對(duì)于HotSpot來說,方法區(qū)也稱為永久代践盼,GC分代手機(jī)擴(kuò)展至方法區(qū)鸦采,或者說使用永久帶來實(shí)現(xiàn)方法區(qū),這樣垃圾收集器就可以像管理堆一樣管理方法區(qū)咕幻,省去專門設(shè)計(jì)方法區(qū)內(nèi)存管理(永久代可以使用-XX:MaxPermSize來設(shè)置上限)渔伯。在JDK7后,把原本放在永久代的字符串常量池移出肄程。

垃圾收集在這塊區(qū)域比較少出現(xiàn)锣吼,這塊區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載。

運(yùn)行時(shí)常量池

是方法區(qū)的一部分蓝厌。Class文件中除了有類的版本玄叠、字段、方法拓提、接口等描述信息外读恃,還有一項(xiàng)信息就是常量池,用于存放編譯期生成的各種字面常量和符號(hào)引用代态,這部分內(nèi)容在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放寺惫。

運(yùn)行時(shí)常量池相對(duì)于Class文件常量池的另外一個(gè)重要特征是具備動(dòng)態(tài)性,運(yùn)行時(shí)期有可能將新的常量放入池中蹦疑,如String的intern()方法西雀。

對(duì)象創(chuàng)建

1、虛擬機(jī)在遇到一條new指令時(shí)歉摧,首相將去檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類的符號(hào)引用蒋搜,并且檢查這個(gè)符號(hào)引用代表的類是否已經(jīng)貝加載篡撵、解析和初始化過,如果沒有先去加載類豆挽;

2育谬、在類加載檢查通過后,虛擬機(jī)將為新生對(duì)象分配內(nèi)存帮哈。對(duì)象所需內(nèi)存的大小在類加載完成后便可完全確定膛檀,為對(duì)象分配空間的任務(wù)等同于把一塊確定大小的內(nèi)存從堆中劃分出來:
假設(shè)堆中的內(nèi)存絕對(duì)規(guī)整,所有分配內(nèi)存放在一邊娘侍,所有空閑內(nèi)存放在另一邊咖刃,通過一指針作為分界點(diǎn)的指示器,這種方式成為“指針碰撞”憾筏;
如果堆內(nèi)存不規(guī)整嚎杨,已使用和空閑的內(nèi)存相互交錯(cuò),那么虛擬機(jī)就要維護(hù)一個(gè)列表氧腰,記錄下哪些內(nèi)存塊可用枫浙,在分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例,并更新列表上的記錄古拴,這種分配方式成為“空閑列表”箩帚;
劃分可用空間(分配內(nèi)存)還有一個(gè)需要考慮的問題:對(duì)象創(chuàng)建在虛擬機(jī)中是非常頻繁的行為,即使是僅僅修改一個(gè)指針?biāo)赶虻奈恢没苹荆诓l(fā)的情況下也并不是線程安全的(可能在給對(duì)象A分配內(nèi)存指針還沒來得及修改時(shí)紧帕,對(duì)象B又同時(shí)使用了原來的指針來分配內(nèi)存的情況),解決方案有兩種:
對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理--實(shí)際上虛擬機(jī)采用CAS分配失敗重試的方式保證更新操作的原自性桅打;
另一種是把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間之中進(jìn)行是嗜,即每個(gè)線程在堆中預(yù)先分配一小塊的內(nèi)存,成為TLAB挺尾,哪個(gè)線程需要分配內(nèi)存就在哪個(gè)線程的TLAB上分配鹅搪,當(dāng)TLAB用完并分配新的TLAB時(shí)才需要同步鎖定(--XX:+/-UseTLAB參數(shù)設(shè)定)。

3潦嘶、內(nèi)存分配完成后涩嚣,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零值(不包括對(duì)象頭),如果使用了TLAB掂僵,也可提前在TLAB分配時(shí)進(jìn)行

4航厚、虛擬機(jī)對(duì)對(duì)象進(jìn)行必要的設(shè)置,例如這個(gè)對(duì)象是哪個(gè)類的實(shí)例锰蓬、如何才能找到元數(shù)據(jù)信息幔睬、對(duì)象的哈希碼、對(duì)象的GC分代年齡等信息芹扭,這些信息都存放在對(duì)象的對(duì)象頭中

5麻顶、至此赦抖,從虛擬機(jī)的視角來看,一個(gè)新的對(duì)象已經(jīng)產(chǎn)生辅肾,但從java程序的視角來看队萤,對(duì)象的創(chuàng)建才剛剛開始--<init>方法還沒執(zhí)行,所有的字段都為零矫钓。一般來說(字節(jié)碼中是否跟隨invokespecial指令決定)要尔,執(zhí)行new指令之后會(huì)接著執(zhí)行<init>方法,把對(duì)象按照程序員的意愿進(jìn)行初始化新娜,這樣子一個(gè)真正可用的對(duì)象才算完全產(chǎn)生出來

對(duì)象的內(nèi)存布局

在HotSpot虛擬機(jī)中赵辕,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為:對(duì)象頭、實(shí)例數(shù)據(jù)和補(bǔ)充對(duì)齊

對(duì)象頭包含兩部分信息:一部分用戶存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)概龄,如哈希碼还惠、GC分代年齡、鎖狀態(tài)標(biāo)志私杜、線程持有的鎖蚕键、偏向線程ID、偏向時(shí)間戳等歪今,這部分被官方稱為“Mark Word”嚎幸。對(duì)象要存儲(chǔ)的數(shù)據(jù)很多颜矿,可能超出32位寄猩、64位Bitmap結(jié)構(gòu)所能記錄的限度,但是對(duì)象頭信息是對(duì)象自身定義的數(shù)據(jù)無關(guān)的額外存儲(chǔ)成本骑疆,被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)田篇,以便在極小的空間內(nèi)存儲(chǔ)盡量多的信息,它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間箍铭,如在32位的HotSpot中泊柬,如果對(duì)象處于未鎖定狀態(tài),那么32位中的25位用來存儲(chǔ)hash诈火,4bit用來存儲(chǔ)對(duì)象分代年齡兽赁,2bit用來存儲(chǔ)鎖標(biāo)識(shí)為,1bit固定0冷守;
另外一部分是類型指針刀崖,即指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例拍摇。如果對(duì)象是一個(gè)數(shù)組亮钦,那么在對(duì)象頭中還有一塊用于記錄數(shù)組長度的數(shù)據(jù)。

實(shí)例數(shù)據(jù):對(duì)象的真正存儲(chǔ)的有效信息充活,也是程序代碼中所定義的各類型的字段內(nèi)容蜂莉。無論是從父類繼承下來的還是在子類中定義的蜡娶,都需要記錄起來。這部分的存儲(chǔ)順序受虛擬機(jī)分配策略和字段在java源碼中定義順序的影響映穗。HotSpot默認(rèn)的分配策略是相同寬度的字段總是被分配到一起窖张,在滿足這個(gè)前提的情況下,在父類中定義的變量會(huì)出現(xiàn)在子類之前蚁滋。如果CompactFields參數(shù)值位true荤堪,那么子類總較窄的變量也可能會(huì)插入到父類變量的空隙中。

對(duì)齊填充:期占位符作用枢赔。因?yàn)镠otSpot自動(dòng)內(nèi)存管理系統(tǒng)要求對(duì)象起始地址必須是8字節(jié)的整數(shù)倍澄阳。

對(duì)象的訪問定位

建立對(duì)象是為了使用對(duì)象,java程序需要通過棧上的reference數(shù)據(jù)來操作堆上的具體對(duì)象踏拜。目前主流的訪問方式有句柄和直接指針兩種:

通過句柄訪問

如果使用句柄訪問碎赢,堆中會(huì)劃分出一塊內(nèi)存來作為句柄池,reference中存儲(chǔ)的就是對(duì)象的句柄地址速梗,而句柄包含了對(duì)象實(shí)例數(shù)據(jù)與數(shù)據(jù)類型各自的具體地址信息

直接指針訪問

如果使用直接指針訪問肮塞,reference中存儲(chǔ)的是對(duì)象地址

使用句柄的好處在于reference中存儲(chǔ)的是穩(wěn)定的句柄地址,在對(duì)象被移動(dòng)(GC移動(dòng)對(duì)象)時(shí)只會(huì)改變句柄中的實(shí)例數(shù)據(jù)指針姻锁,而reference本身不需要修改枕赵;使用直接指針訪問方式的好處是速度更快,節(jié)省了一次指針定位的時(shí)間開銷位隶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拷窜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涧黄,更是在濱河造成了極大的恐慌篮昧,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笋妥,死亡現(xiàn)場離奇詭異懊昨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)春宣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門酵颁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人月帝,你說我怎么就攤上這事躏惋。” “怎么了嫁赏?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵其掂,是天一觀的道長。 經(jīng)常有香客問我潦蝇,道長款熬,這世上最難降的妖魔是什么深寥? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮贤牛,結(jié)果婚禮上惋鹅,老公的妹妹穿的比我還像新娘。我一直安慰自己殉簸,他們只是感情好闰集,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著般卑,像睡著了一般武鲁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蝠检,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天沐鼠,我揣著相機(jī)與錄音,去河邊找鬼叹谁。 笑死饲梭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的焰檩。 我是一名探鬼主播憔涉,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼析苫!你這毒婦竟也來了兜叨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤藤违,失蹤者是張志新(化名)和其女友劉穎浪腐,沒想到半個(gè)月后纵揍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顿乒,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年泽谨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了璧榄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吧雹,死狀恐怖骨杂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雄卷,我是刑警寧澤搓蚪,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站丁鹉,受9級(jí)特大地震影響妒潭,放射性物質(zhì)發(fā)生泄漏悴能。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一雳灾、第九天 我趴在偏房一處隱蔽的房頂上張望漠酿。 院中可真熱鬧,春花似錦谎亩、人聲如沸炒嘲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夫凸。三九已至,卻和暖如春阱持,著一層夾襖步出監(jiān)牢的瞬間寸痢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工紊选, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啼止,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓兵罢,卻偏偏與公主長得像献烦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卖词,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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