JVM筆記02-JVM內(nèi)存區(qū)域結構

0. 前言

JVM筆記系列伞剑,以JDK1.7為基準分歇,主要以《深入理解Java虛擬機》(第二版)和《Java虛擬機規(guī)范(Java SE 7版)》 為參考俗他,主要包括下圖所示的五部分內(nèi)容:1.類加載脐区,2.內(nèi)存區(qū)域块促,3.垃圾回收佃牛,4.JVM參數(shù)淹辞,5.JVM監(jiān)控工具。

本人是Java程序員俘侠,重點關注這些有助于優(yōu)化開發(fā)桑涎、性能調(diào)優(yōu)、問題解決等這些和具體生產(chǎn)密切相關的部分兼贡;關于Class的文件結構攻冷、編譯、指令等部分遍希,可以閱讀上述書籍或其它材料等曼。

jvm.png

本文主要記錄JVM內(nèi)存區(qū)域結構的相關知識,本文的主要知識點如下:

jvm內(nèi)存區(qū)域結構.png

1. JVM內(nèi)存區(qū)域結構

JVM定義了若干程序運行期使用到的數(shù)據(jù)區(qū),其中一些隨著JVM進程啟動而創(chuàng)建禁谦,隨著JVM退出而銷毀胁黑;另一些則是與線程一一對應,隨著線程的啟動和結束而建立和銷毀州泊。JVM的運行時數(shù)據(jù)區(qū)分為5個部分丧蘸,如下圖所示,分別是程序計數(shù)器遥皂、Java棧力喷、Native方法棧、堆演训、方法區(qū)弟孟。

jvm-runtime-area.png

1.1 程序計數(shù)器(Program Counter)

  • 程序計數(shù)器占用非常小的內(nèi)存,指向下一條指令的地址样悟。
  • 每個線程擁有一個程序計數(shù)器拂募。
  • 程序計數(shù)器在線程創(chuàng)建時創(chuàng)建。
  • 如果是Java方法窟她,程序計數(shù)器指向字節(jié)碼指令的地址陈症。
  • 如果是Native方法,程序計數(shù)器值則為空(Undefined)震糖。
  • 程序計數(shù)器不會出現(xiàn)OutOfMemoryError爬凑。

1.2 Java棧

  • Java棧是線程私有的,生命周期和線程相同试伙。
  • 棧是由一系列棧幀組成的。
  • 每個棧幀用于存儲局部變量表于样、操作數(shù)棧疏叨、動態(tài)鏈接、方法出口等信息穿剖。
  • 每一個方法被調(diào)用到執(zhí)行完成的過程蚤蔓,就是一個棧幀在JVM從入棧到出棧的過程。
jvm-stack-frame.png

JVM規(guī)范中描述糊余,Java椥阌郑可能會出現(xiàn)兩種異常。

  • StackOverflowError:線程請求的棧深大于虛擬機所允許的深度(例如無限遞歸)贬芥。
  • OutOfMemoryError:虛擬機椡抡蓿可以動態(tài)擴展,如果擴展無法申請足夠的內(nèi)存時蘸劈,就會報出昏苏。

1.3 Native方法棧

本地方法棧和Java棧是非常相似的,Java棧是為了執(zhí)行Java方法服務,本地方法棧是為了執(zhí)行Native方法使用贤惯。在HotSpot虛擬機中洼专,Java棧和本地方法棧合二為一。

1.4 堆(Heap)

  • Java堆是JVM所管理的內(nèi)存中最大的一塊孵构,生命周期和JVM進程相同屁商。
  • 用于存放對象實例,幾乎所有的對象都在Heap上颈墅。
  • Java堆是所有線程共享的空間蜡镶。

從垃圾回收的角度來說,Java堆分為新生代和老生代精盅,其中新生代還分為Eden帽哑、From Survivor(S0)、To Survivor(S1)三部分叹俏,如下圖所示妻枕。


jvm-heap.png

默認參數(shù)下,新生代:老生代 = 1:2粘驰,Eden:Survivor = 8:1屡谐。Java堆中最大可用內(nèi)存 = 老生代+ Eden + Survivor*1,即S0和S1永遠有一個處于閑置的狀態(tài)蝌数,GC的時JVM候會把其中一個Survivor中存活的對象復制到另一個Survivor中愕掏。

  • Eden區(qū)是Java實例對象優(yōu)先分配的區(qū)域,如果Eden沒有足夠的空間顶伞,將會執(zhí)行一次Minor GC饵撑。
  • 經(jīng)過Minor GC后,Eden+S0(或者S1)中還存活的對象將會轉移到S1中唆貌,然后S0會被清空滑潘。
  • Survivor中放不下的、存活次數(shù)超過一定數(shù)目的對象锨咙,會被轉移到老年代(Old)空間语卤,大對象也可能會直接分配到老年代(Old)空間。
  • 當老年代(Old)空間不夠時酪刀,將會發(fā)生Major GC粹舵。
  • 如果垃圾回收后,仍然沒有足夠的空間骂倘,那么將會拋出OutOfMemoryError眼滤。

1.5 方法區(qū)

  • 方法區(qū)是線程共享的空間,生命周期和JVM進程相同历涝。
  • 方法區(qū)用于存儲類的信息柠偶、常量池情妖、字段和方法數(shù)據(jù)、字節(jié)碼內(nèi)容等诱担。

在我們常用的HotSpot虛擬機中毡证,JDK1.7之前,使用PermGen(永久代)來實現(xiàn)方法區(qū)蔫仙;在JDK1.8中完全移除了PermGen料睛,改用Metaspace(元空間)來實現(xiàn)方法區(qū)。

其實摇邦,移除PermGen的工作從JDK1.7就開始了恤煞,符號引用(Symbols)、字面量(interned strings)施籍、類的靜態(tài)變量(class statics)在1.7中都轉移到了Heap中居扒,這大大減少了PermGen拋出OutOfMemoryError的機會。

Metaspace使用的是本地內(nèi)存丑慎,而非JVM內(nèi)存喜喂;因此Metaspace的大小限制,受限于物理內(nèi)存的的限制竿裂;當然它是可以通過參數(shù)-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 來指定的玉吁。

方法區(qū)的空間不夠用了,將會拋出OutOfMemoryError腻异。

關于方法區(qū)进副,運行時常量池特別值得一提,運行時常量池中的常量悔常,基本來源于各個class文件中的常量池影斑;程序運行時,除非手動向常量池中添加常量(比如調(diào)用String.intern方法)机打,否則jvm不會自動添加常量到常量池矫户。

1.6 直接內(nèi)存(Direct Memory)

直接內(nèi)存并不是JVM運行時數(shù)據(jù)區(qū)的一部分,屬于堆外(off-heap)內(nèi)存姐帚,也不是JVM規(guī)范中定義的內(nèi)存區(qū)域。JDK1.4新增了NIO包障涯,引入了一種基于Channel和Buffer的IO方式罐旗,可以使用Native方法直接分配堆外內(nèi)存,然后通過存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作唯蝶。

//見 java.nio.ByteBuffer
public static ByteBuffer allocateDirect(int capacity) {
    return new DirectByteBuffer(capacity);
}

// native方法九秀,見sun.misc.Unsafe類
public native long allocateMemory(long var1);

使用堆外內(nèi)存,可以擴展使用更大的內(nèi)存空間粘我,理論上能減少GC的暫停時間鼓蜒,還可以在進程間共享(MappedByteBuffer和FileChannel)痹换。

Direct Memory默認的大小是等同于JVM最大堆,我們可以通過-XX:MaxDirectMemorySize參數(shù)來控制其大小都弹。

如果直接內(nèi)存空間不夠用了娇豫,將會拋出OutOfMemoryError。

2. 對象的創(chuàng)建和訪問過程

2.1 對象的創(chuàng)建過程

  1. 類加載檢測畅厢。當new對象的時候冯痢,將會檢查能否在常量池中定位到一個類的符號引用,并檢查這個類是否被加載框杜、解析和初始化浦楣,如果沒有,則執(zhí)行相應的類加載過程咪辱。

  2. 類加載檢查通過后振劳,JVM將會為新生的對象分配內(nèi)存。如果Java堆內(nèi)存是規(guī)整的油狂,內(nèi)存分配采用“指針碰撞”方式历恐;如果內(nèi)存不是規(guī)整的,則采用“空閑列表”的方式选调。Java堆內(nèi)存是否規(guī)整夹供,取決于使用的垃圾回收器是否帶有壓縮整理的功能。因此仁堪,在使用Serial哮洽、ParNew等帶Compact過程的收集器時,系統(tǒng)采用的分配算法是指針碰撞弦聂,而使用CMS這種基于Mark-Sweep算法的收集器時鸟辅,通常采用空閑列表。給對象分配內(nèi)線的過程莺葫,是指針移動的過程匪凉,它不是線程安全的,需要同步捺檬;為了解決這個問題再层,JVM給每個線程在Java堆中預先分配一塊內(nèi)存,稱為本地線程分配緩沖(Thread Local Allocation Buffer,TLAB)堡纬,這樣以來聂受,只有緩沖區(qū)用完了,重新分配時才需要同步操作烤镐。

  3. 對象內(nèi)存分配完畢之后蛋济,JVM把分配的內(nèi)存空間都初始化為零值。

  4. JVM對對象做必要的設置炮叶。例如對象是哪個類的實例碗旅、如何找到類的元數(shù)據(jù)渡处、對象的哈希碼、對象的GC分代年齡等祟辟,這些信息存放在對象頭(Object Header)中医瘫。

  5. 至此,在JVM看來對象創(chuàng)建完成川尖;接下來執(zhí)行<init>方法登下,把對象按照程序員的意愿初始化,形成一個真正可用的對象叮喳。

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

對象在堆中的布局分為三個區(qū)域:對象頭被芳,實例數(shù)據(jù),對齊填充馍悟。

  • 對象頭 包括兩個部分畔濒,第一部分是“Mark Word”,用于存儲對象自身的運行時數(shù)據(jù)锣咒,包括HashCode侵状、GC分代年齡、鎖狀態(tài)標志毅整、線程持有的鎖趣兄、偏向ID、偏向時間戳等悼嫉;第二部分是類型指針艇潭,指向存放指向方法區(qū)的類數(shù)據(jù),即JVM通過這個指針來確定對象是哪個類的實例戏蔑。

  • 實例數(shù)據(jù) 存放類的屬性蹋凝,包括父類的屬性信息。相同寬度的字段(例如long和double都是8字節(jié))分配在一起总棵。

  • 對齊填充 這是虛擬機要求對象起始地址必須是8字節(jié)的整數(shù)倍鳍寂,如果實例數(shù)據(jù)部分不是8字節(jié)的整數(shù)倍,那么就需要對齊填充來補齊情龄,除此之外迄汛,并無它意。

2.3 對象的訪問定位

引用存放在Java棧上骤视,數(shù)據(jù)類型為reference鞍爱;對象存放在Java堆中,引用是如何指向對象實例呢尚胞?

目前主流的訪問方式有兩種硬霍,1.使用句柄帜慢;2.使用直接指針笼裳。

如果使用句柄訪問唯卖,那么Java堆中將會分出一塊內(nèi)存作為句柄池,reference中存儲的就是對象的句柄地址躬柬,句柄中包含了對象實例數(shù)據(jù)和類型數(shù)據(jù)的具體地址拜轨。句柄的好處在于,當對象被移動時(垃圾回收時發(fā)生)允青,只會改變句柄中的實例數(shù)據(jù)指針橄碾,reference本身不需要修改。

對象句柄訪問.png

如果使用直接指針訪問颠锉,reference引用直接指向堆中的對象實例法牲,對象實例的對象頭存放對象類型指針,這種方式的好處在于琼掠,減少了一次指針定位的開銷拒垃,訪問速度更快。

對象指針訪問.png

HotSpot虛擬機中使用的是直接指針訪問的方式瓷蛙。

(完)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悼瓮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子艰猬,更是在濱河造成了極大的恐慌横堡,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冠桃,死亡現(xiàn)場離奇詭異命贴,居然都是意外死亡,警方通過查閱死者的電腦和手機腊满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門套么,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碳蛋,你說我怎么就攤上這事胚泌。” “怎么了肃弟?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵玷室,是天一觀的道長。 經(jīng)常有香客問我笤受,道長穷缤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任箩兽,我火速辦了婚禮津肛,結果婚禮上,老公的妹妹穿的比我還像新娘汗贫。我一直安慰自己身坐,他們只是感情好秸脱,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著部蛇,像睡著了一般摊唇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涯鲁,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天巷查,我揣著相機與錄音,去河邊找鬼抹腿。 笑死岛请,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的警绩。 我是一名探鬼主播髓需,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼房蝉!你這毒婦竟也來了僚匆?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤搭幻,失蹤者是張志新(化名)和其女友劉穎咧擂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檀蹋,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡松申,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了俯逾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贸桶。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖桌肴,靈堂內(nèi)的尸體忽然破棺而出皇筛,到底是詐尸還是另有隱情,我是刑警寧澤坠七,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布水醋,位于F島的核電站,受9級特大地震影響彪置,放射性物質發(fā)生泄漏拄踪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一拳魁、第九天 我趴在偏房一處隱蔽的房頂上張望惶桐。 院中可真熱鬧,春花似錦、人聲如沸姚糊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叛拷。三九已至,卻和暖如春岂却,著一層夾襖步出監(jiān)牢的瞬間忿薇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工躏哩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留署浩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓扫尺,卻偏偏與公主長得像筋栋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子正驻,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

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