深入理解jvm之java內(nèi)存模型

java虛擬機棧:

每個方法執(zhí)行時創(chuàng)建棧幀明未,存儲局部變量表槽华,操作數(shù)棧,動態(tài)鏈接趟妥,方法出入口等信息猫态。一個方法調(diào)用到完成過程,就是一個棧幀在虛擬機棧中入到到出棧過程披摄。
局部變量表存放編譯器可知的基本數(shù)據(jù)類型亲雪、對象引用(reference類型)和returnAddress類型(指向了一條字節(jié)碼指令的地址)
64位的long和double類型占用兩個局部變量空間(slot),其余類型只占用一個疚膊。局部變量表需要內(nèi)存空間在編譯期間完成分配义辕,進入一個方法時,這個方法需要在幀中分配多大局部變量表示確定的寓盗,方法運行期間不會改變局部變量表大小灌砖。
在棧區(qū)域規(guī)定了兩種異常:
如果線程請求棧深度大于虛擬機允許的深度,拋出stackoverflowerror異常
如果虛擬機可以動態(tài)擴展(大部分都可以)傀蚌,如果擴展時無法申請到足夠內(nèi)存基显,拋出outofmemoryerror異常

java堆:

可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可善炫。通過-Xms -Xmx實現(xiàn)可擴展撩幽。如果堆中沒有內(nèi)存完成實例分配,并且堆無法再擴展時销部,拋出oom異常

方法區(qū):

和堆一樣摸航,各個線程共享,存儲已經(jīng)被虛擬機加載的類信息舅桩、常量酱虎、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)擂涛。java虛擬機規(guī)范將方法區(qū)描述為堆的一個邏輯部分读串,但是它卻有一個別名Non-heap(非堆),目的應(yīng)該是和堆區(qū)分出來撒妈。
方法區(qū)和永久代并不等價恢暖。只是因為hotspot虛擬機設(shè)計團隊選擇將gc分代收集擴展至方法區(qū),或者說用永久代實現(xiàn)方法區(qū)而已狰右,這樣hotspot垃圾收集器可以像管理java堆一樣管理這部分內(nèi)存杰捂,省去專門為方法區(qū)編寫內(nèi)存管理的代碼。對其他虛擬機(如bea jrockit)不存在永久代概念棋蚌。
使用永久代實現(xiàn)方法區(qū)并不合適嫁佳,容易內(nèi)存溢出挨队,永久代有-XX:MaxPermSize的上限,J9和JRockit只要沒到進程可用內(nèi)存上限(如32位4Gb)就不會有問題蒿往。而從jdk1.7的hotspot開始盛垦,已經(jīng)把原本在永久代的字符串常量池移出。
方法區(qū)和堆一樣不需要連續(xù)內(nèi)存瓤漏,可以選擇固定大小或可擴展腾夯,另外還可以選擇不實現(xiàn)垃圾回收。無法滿足內(nèi)存分配拋出oom.

運行時常量池:

方法區(qū)一部分蔬充。class文件中除了有類版本蝶俱,字段,方法娃惯,接口等描述信息外跷乐,還有一項信息是常量池(constant pool table)肥败,用于存放編譯期生成的字面量和符號引用趾浅,這部分將在類加載后進入方法區(qū)的運行時常量池中存放。
運行時常量池相對class常量池一個特征就是具有動態(tài)性馒稍,java并不要求常量只有編譯期才能產(chǎn)生皿哨,就是并非預(yù)置入class文件中常量池的內(nèi)容才能進入方法區(qū)運行時常量池,運行期間也可能將新的常量放入池中纽谒,比如String類的intern()方法证膨。

直接內(nèi)存:

直接內(nèi)存(Direct Memory)不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是java虛擬機規(guī)范中定義的內(nèi)存區(qū)域鼓黔,但這部分內(nèi)存也可能導致oom央勒。
jdk1.4中加入了nio(new input/output)類,引入了基于通道(channel)與緩沖區(qū)(buffer)的I/O方法澳化,使用native函數(shù)庫分配堆外內(nèi)存崔步,通過java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。在一些場合可以提高性能缎谷,因為避免了java堆和native堆中來回復(fù)制數(shù)據(jù)井濒。

對象的創(chuàng)建

虛擬機遇到一條new指令時,首先將去檢查這個指令是否能在常量池中定位到一個類的符號引用列林,并且檢查這個符號引用代表的類是否已經(jīng)被加載瑞你、解析和初始化過,如果沒有希痴,那必須先執(zhí)行相應(yīng)的類加載過程者甲。在類加載檢查通過后,虛擬機將為新生對象分配內(nèi)存砌创,對象所需的內(nèi)存大小在類加載完成后便可以確定虏缸。
java劃分堆的方式:
指針碰撞:如果java堆內(nèi)存規(guī)整甥厦,用過的內(nèi)存放一邊,空閑的另一邊寇钉,中間放指針做分界點指示器刀疙,那內(nèi)存分配就只需要向空閑區(qū)挪動指針和對象大小相等距離。
空閑列表:不規(guī)整扫倡,維護一個列表谦秧,記錄那些內(nèi)存塊可用。
java堆是否規(guī)整由垃圾收集器是否由壓縮整理功能決定撵溃。使用Serial,parnew帶compact過程的收集器時疚鲤,采用指針碰撞。使用cms這種基于mark-sweep算法的收集器缘挑,采用空閑列表集歇。

并發(fā)情況下移動指針有安全問題:
1.對分配內(nèi)存空間動作進行同步處理,使用cas配失敗重試保證更新原子性语淘。
2.把內(nèi)存分配的動作按照線程劃分在不同空間中進行诲宇,每個線程在java堆中分配一小塊內(nèi)存,稱為本地線程分配緩沖(Thread Local Allocation Buffer,TLAB)惶翻。哪個線程要分配內(nèi)存姑蓝,就在哪個線程的TLAB上分配,只有TLAB用完并分配新的TLAB時吕粗,才需要同步鎖定纺荧。是否使用TLAB通過-XX:+/-UseTLAB參數(shù)來設(shè)定。

JVM在內(nèi)存新生代Eden Space中開辟了一小塊線程私有的區(qū)域颅筋,稱作TLAB(Thread-local allocation buffer)宙暇。默認設(shè)定為占用Eden Space的1%。在Java程序中很多對象都是小對象且用過即丟议泵,它們不存在線程共享也適合被快速GC占贫,所以對于小對象通常JVM會優(yōu)先分配在TLAB上,并且TLAB上的分配由于是線程私有所以沒有鎖開銷肢簿。因此在實踐中分配多個小對象的效率通常比分配一個大對象的效率要高靶剑。
也就是說,Java中每個線程都會有自己的緩沖區(qū)稱作TLAB(Thread-local allocation buffer)池充,每個TLAB都只有一個線程可以操作桩引,TLAB結(jié)合bump-the-pointer技術(shù)可以實現(xiàn)快速的對象分配,而不需要任何的鎖進行同步收夸,也就是說坑匠,在對象分配的時候不用鎖住整個堆,而只需要在自己的緩沖區(qū)分配即可卧惜。

接下來厘灼,虛擬機對對象進行必要的設(shè)置夹纫,如對象是哪個類實例,如何找到類元數(shù)據(jù)信息设凹,對象哈希碼舰讹,對象GC分代年齡等信息。這些信息存放在對象頭中(Object Header).
上面工作完成后闪朱,從虛擬機視角看月匣,一個新對象已經(jīng)產(chǎn)生,但java視角對象創(chuàng)建才開始奋姿,<init>方法還未執(zhí)行锄开,字段還為零。所以一般來說(由字節(jié)碼是否跟隨invokespecial指令決定)称诗,new指令后會接著執(zhí)行<init>方法初始化萍悴,可用對象才算產(chǎn)生。

對象結(jié)構(gòu)

hotspot虛擬機中寓免,對象分為三塊區(qū)域:對象頭(Header),實例數(shù)據(jù)(Instance Data),對齊填充(Padding)
對象頭包括兩部分:1.用于存儲對象自身的運行時數(shù)據(jù)癣诱。如哈希碼,GC分代年齡再榄,鎖狀態(tài)標志狡刘,線程持有的鎖,偏向線程ID,偏向時間鎖等创肥。官方稱為Mark Word
2.類型指針徙赢。即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針確定這個對象是哪個類的實例肩碟。
對齊填充僅僅占位符作用,用于補齊沒有對齊的實例數(shù)據(jù)部分(對象大小必須8字節(jié)整數(shù)倍)。

對象的訪問定位

句柄:會在堆中劃分句柄池猬腰,ref存儲對象的句柄池地址,句柄中包括對象實例數(shù)據(jù)和類型數(shù)據(jù)的地址猜敢。
直接指針:ref存儲的直接就是對象地址姑荷。
句柄好處:對象移動(gc)時候只需要改變句柄的實例數(shù)據(jù)指針,ref本身不變缩擂。
直接指針:訪問速度快鼠冕,節(jié)省一次指針定位開銷。hotspot使用這種胯盯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懈费,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子博脑,更是在濱河造成了極大的恐慌憎乙,老刑警劉巖票罐,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泞边,居然都是意外死亡该押,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門阵谚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沈善,“玉大人,你說我怎么就攤上這事椭蹄∥拍担” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵绳矩,是天一觀的道長罩润。 經(jīng)常有香客問我,道長翼馆,這世上最難降的妖魔是什么割以? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮应媚,結(jié)果婚禮上严沥,老公的妹妹穿的比我還像新娘。我一直安慰自己中姜,他們只是感情好消玄,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丢胚,像睡著了一般翩瓜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上携龟,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天兔跌,我揣著相機與錄音,去河邊找鬼峡蟋。 笑死坟桅,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蕊蝗。 我是一名探鬼主播仅乓,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匿又!你這毒婦竟也來了方灾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裕偿,沒想到半個月后洞慎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡嘿棘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年劲腿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟妙。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡焦人,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出重父,到底是詐尸還是另有隱情花椭,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布房午,位于F島的核電站矿辽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏郭厌。R本人自食惡果不足惜袋倔,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望折柠。 院中可真熱鬧宾娜,春花似錦、人聲如沸扇售。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘眶。三九已至嘱根,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巷懈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工慌洪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顶燕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓冈爹,卻偏偏與公主長得像涌攻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子频伤,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

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