深入JVM-java虛擬機(jī)的基本結(jié)構(gòu)

本文將介紹Java虛擬機(jī)的基本結(jié)構(gòu),各組成部分的作用烁试,以及相互之間是如何協(xié)調(diào)的雇初。而要了解這些,首先必須了解Java堆减响、Java棧靖诗、永久區(qū)和元數(shù)據(jù)區(qū)的基本概念郭怪。

一、Java虛擬機(jī)的架構(gòu)

java虛擬機(jī)的基本結(jié)構(gòu)

1.1 類加載子系統(tǒng)

類加載子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載Class信息呻畸,加載的類信息放在一塊稱為方法區(qū)的內(nèi)存空間移盆。除了類的信息外,方法區(qū)中還會(huì)存放運(yùn)行時(shí)常量池的信息伤为,包括字符串字面量和數(shù)字常量(這部分常量信息是class文件中常量池部分的內(nèi)存映射)咒循。

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

程序計(jì)數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,他可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器绞愚。在虛擬機(jī)的概念模型里(僅是概念模型叙甸,各種虛擬機(jī)可能會(huì)通過一些更高效的方式去實(shí)現(xiàn)),字節(jié)碼解釋器工作時(shí)就說通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令位衩,分支裆蒸、循環(huán)、跳轉(zhuǎn)糖驴、異常處理僚祷、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。

由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的贮缕,在任何一個(gè)確定的時(shí)刻辙谜,一個(gè)處理器(對于多核處理器來說是一個(gè)內(nèi)核)都只會(huì)執(zhí)行一條線程中的指令。因此感昼,為了線程切換后能恢復(fù)到正確的執(zhí)行位置装哆,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間計(jì)數(shù)器互不影響定嗓,獨(dú)立存儲(chǔ)蜕琴,我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存。

如果線程正在執(zhí)行的是一個(gè)Java方法宵溅,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址:如果正在執(zhí)行的是Native方法凌简,這個(gè)計(jì)數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域恃逻。

1.3 Java虛擬機(jī)棧

Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有的雏搂,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表辛块、操作數(shù)棧、動(dòng)態(tài)鏈接铅碍、方法出口等信息润绵。每一個(gè)方法從調(diào)用直至執(zhí)行完成,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程胞谈。

局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean尘盼、byte憨愉、char、short卿捎、int配紫、float、long午阵、double)躺孝、對象引用(reference類型,他不等同于對象本身底桂,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔樦才郏部赡苁侵赶蛞粋€(gè)代表對象的句柄或其他與此對象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

在Java虛擬機(jī)規(guī)范中籽懦,對這個(gè)區(qū)域規(guī)定了兩種異常情況:如果線程請求的棧深度大于虛擬機(jī)所允許的深度于个,將拋出StackOverFlowError異常;如果虛擬機(jī)椖核常可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展厅篓,只不過Java虛擬機(jī)規(guī)范中也允許固定長度的虛擬機(jī)棧),如果擴(kuò)展時(shí)無法申請到足夠的內(nèi)存捶码,就會(huì)拋出OutOfMemoryError異常羽氮。

1.4 本地方法棧

與虛擬機(jī)棧的作用相似,他們之間的區(qū)別是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)宙项,而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)乏苦。

1.5 Java堆

Java堆(Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域尤筐,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建汇荐。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存盆繁。

Java堆是垃圾收集器管理的主要區(qū)域掀淘,因此很多時(shí)候被稱為GC堆。由于現(xiàn)在收集器基本都采用分代收集算法油昂,所以Java堆中還可以細(xì)分為:新生代和老年代革娄;再細(xì)致一點(diǎn)的有Eden空間、From Survivor空間冕碟、To Survivor空間等拦惋。

1.6 方法區(qū)

與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域安寺,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息厕妖、常量、靜態(tài)變量挑庶、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)言秸。

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

運(yùn)行時(shí)常量池是方法區(qū)的一部分软能。Class文件中除了有類的版本、字段举畸、方法查排、接口等描述信息外,還有一項(xiàng)信息是常量池抄沮,用于存放編譯期生成的各種字面量和符號(hào)引用跋核,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

運(yùn)行時(shí)常量池相對于Class文件常量池的另外一個(gè)重要特征是ju'bei具備動(dòng)態(tài)性合是,Java語言并不要求常量一定只有編譯期才能產(chǎn)生了罪,也就是并非預(yù)置入Class文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中聪全,這種特性被開發(fā)人員利用的比較多的便是String類的intern()方法泊藕。

1.8 直接內(nèi)存

直接內(nèi)存并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域难礼。但是這部分內(nèi)存也被頻繁的使用娃圆,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。它直接在Java堆外蛾茉、直接向系統(tǒng)申請的內(nèi)存空間讼呢。通常,訪問直接內(nèi)存的速度會(huì)優(yōu)于Java堆谦炬。因此悦屏,在讀寫頻繁的場合可能會(huì)考慮使用直接內(nèi)存。由于直接內(nèi)存在Java堆外键思,因此它的大小不會(huì)直接受限于Xmxd指定的最大堆大小础爬,但是系統(tǒng)內(nèi)存是有限的,Java堆和直接內(nèi)存的總和依然受限于操作系統(tǒng)能給出的最大內(nèi)存吼鳞。

在JDK 1.4中新加入了NIO類看蚜,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,他可以使用Native函數(shù)庫直接分配堆外內(nèi)存赔桌,然后通過一個(gè)存儲(chǔ)在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作供炎。這樣能在一些場景中顯著提高性能,因?yàn)楸苊饬嗽贘ava堆和Native堆中來回復(fù)制數(shù)據(jù)疾党。

二音诫、認(rèn)識(shí)Java堆

Java堆是和Java應(yīng)用程序關(guān)系最為密切的內(nèi)存空間,幾乎所有的對象都存放在堆中雪位。并且Java堆是完全自動(dòng)化管理的竭钝,通過垃圾回收機(jī)制,垃圾對象會(huì)被自動(dòng)清理,而不需要顯式的釋放蜓氨。

根據(jù)垃圾回收機(jī)制的不同,Java堆有可能擁有不同的結(jié)構(gòu)队伟,最常見的一種是將Java堆分為新生代和老年代穴吹。其中,新生代存放新生對象或者年齡不大的對象嗜侮,老年代存放老年對象港令。新生代可能分為eden區(qū)、s0區(qū)锈颗、s1區(qū)顷霹,s0和s1也被稱為from和to區(qū)域,他們是兩塊大小相等击吱、可以互換角色的內(nèi)存空間淋淀。

在絕大多數(shù)情況下,對象首先分配在eden區(qū)覆醇,在一次新生代回收(Young GC)后朵纷,如果對象還存活,則會(huì)進(jìn)入s0或s1永脓,之后袍辞,沒經(jīng)過一次Young GC,對象如果存活常摧,他的年齡就會(huì)加1.當(dāng)對象的年齡達(dá)到一定條件后搅吁,就會(huì)被認(rèn)為是老年代,從而進(jìn)入老年代落午。

堆谎懦、方法區(qū)、棧的關(guān)系

三板甘、出入Java棧

Java棧是一塊線程私有的內(nèi)存空間党瓮。如果說,Java堆和程序數(shù)據(jù)密切相關(guān)盐类,那么Java堆就是和線程執(zhí)行密切相關(guān)的寞奸。線程執(zhí)行的基本行為是函數(shù)調(diào)用,每次函數(shù)調(diào)用的數(shù)據(jù)都是通過Java棧傳遞的在跳。

Java棧與數(shù)據(jù)結(jié)構(gòu)上的棧有著類似的含義枪萄,他是一塊先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),只支持出棧和入棧兩種操作猫妙。Java虛擬機(jī)提供了參數(shù)-Xss來指定線程的最大棿煞空間,這個(gè)參數(shù)也直接決定了函數(shù)調(diào)用的最大深度。

3.1 局部變量表

局部變量表是棧幀的重要組成部分之一齐帚。它用于保存函數(shù)的參數(shù)以及局部變量妒牙。局部變量表中的變量只在當(dāng)前函數(shù)調(diào)用中有效,當(dāng)函數(shù)調(diào)用結(jié)束后对妄,隨著函數(shù)棧幀的銷毀湘今,局部變量表也會(huì)隨之銷毀。

由于局部變量表在棧幀之中剪菱,因此摩瞎,如果函數(shù)的參數(shù)和局部變量較多,會(huì)使得局部變量表膨脹孝常,從而每一次函數(shù)調(diào)用就會(huì)占用更多的椘烀牵空間,最終導(dǎo)致函數(shù)的嵌套調(diào)用次數(shù)減少构灸。

局部變量表中的變量也是重要的垃圾回收根節(jié)點(diǎn)上渴,只要被局部變量表中直接或間接引用的對象是不會(huì)被回收的。因此喜颁,理解局部變量表對理解垃圾回收也有一定的幫助驰贷。

可以使用參數(shù)-XX:+PrintGC,在輸出的日志中洛巢,可以看到垃圾回收前后堆的大小括袒。

3.2 操作數(shù)棧

它主要用于保存計(jì)算過程的中間結(jié)果,同時(shí)作為計(jì)算過程中變量臨時(shí)的存儲(chǔ)空間稿茉。

3.3 幀數(shù)據(jù)區(qū)

大部分Java字節(jié)碼指令需要進(jìn)行常量池訪問锹锰,在幀數(shù)據(jù)區(qū)中保存著訪問變量池的指針,方便程序訪問常量池漓库。同時(shí)異常處理表也是幀數(shù)據(jù)區(qū)中重要的一部分恃慧。

3.4 棧上分配

棧上分配是Java虛擬機(jī)提供的一項(xiàng)優(yōu)化技術(shù),他的基本思想是渺蒿,對于那些線程私有的對象(這里指不可能被其他線程訪問的對象)痢士,可以將他們打散分配在棧上,而不是分配在堆上茂装。分配在棧上的好處是可以在函數(shù)調(diào)用結(jié)束后自行銷毀怠蹂,而不需要垃圾回收器的介入,從而提高系統(tǒng)的性能少态。

棧上分配的一個(gè)技術(shù)基礎(chǔ)是進(jìn)行逃逸分析城侧。逃逸分析的目的是判斷對象的作用域是否有可能逃逸出函數(shù)體。

對于大量的零散小對象彼妻,棧上分配提供了一種很好的對象分配優(yōu)化策略嫌佑,棧上分配速度快豆茫,并且可以有效避免垃圾回收帶來的負(fù)面影響,但由于和堆空間相比屋摇,椏辏空間較小,因此對于大對象也不適合在棧上分配炮温。

四肤京、方法區(qū)

方法區(qū)是一塊所有線程共享的內(nèi)存區(qū)域,用于保存系統(tǒng)的類信息茅特,比如類的字段、方法棋枕、常量池等白修。方法去的大小決定了系統(tǒng)可以保存多少類,如果系統(tǒng)定義了太多的類重斑,導(dǎo)致方法區(qū)溢出兵睛,虛擬機(jī)同樣會(huì)拋出內(nèi)存溢出錯(cuò)誤。

在JDK 1.6窥浪、JDK 1.7中祖很,方法區(qū)可以理解為永久區(qū)(Perm)。永久區(qū)可以用參數(shù)-XX:PermSize和-XX:MaxPermSize指定漾脂,默認(rèn)情況下假颇,-XX:MaxPermSize為64M。一個(gè)大的永久區(qū)可以保存更多的類信息骨稿。如果系統(tǒng)使用了一些動(dòng)態(tài)代理笨鸡,那么有可能會(huì)在運(yùn)行時(shí)產(chǎn)生大量的類,如果這樣坦冠,就需要設(shè)置一個(gè)合理的永久區(qū)大小形耗,確保不發(fā)生永久區(qū)內(nèi)存溢出。

在JDK 1.8中辙浑,永久區(qū)已經(jīng)被徹底移除激涤。取而代之的是元數(shù)據(jù)區(qū),元數(shù)據(jù)區(qū)大小可以用參數(shù)-XX:MaxMetaspaceSize指定(一個(gè)大的元數(shù)據(jù)區(qū)可以使系統(tǒng)支持更多的類)判呕,這是一塊堆外的直接內(nèi)存倦踢。與永久區(qū)不同,如果不指定大小侠草,默認(rèn)情況下硼一,虛擬機(jī)會(huì)耗盡所有的可用系統(tǒng)內(nèi)存。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梦抢,一起剝皮案震驚了整個(gè)濱河市般贼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖哼蛆,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕊梧,死亡現(xiàn)場離奇詭異,居然都是意外死亡腮介,警方通過查閱死者的電腦和手機(jī)肥矢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叠洗,“玉大人甘改,你說我怎么就攤上這事∶鹨郑” “怎么了十艾?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腾节。 經(jīng)常有香客問我忘嫉,道長,這世上最難降的妖魔是什么案腺? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任庆冕,我火速辦了婚禮,結(jié)果婚禮上劈榨,老公的妹妹穿的比我還像新娘访递。我一直安慰自己,他們只是感情好同辣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布力九。 她就那樣靜靜地躺著,像睡著了一般邑闺。 火紅的嫁衣襯著肌膚如雪跌前。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天陡舅,我揣著相機(jī)與錄音抵乓,去河邊找鬼。 笑死靶衍,一個(gè)胖子當(dāng)著我的面吹牛灾炭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颅眶,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼蜈出,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涛酗?” 一聲冷哼從身側(cè)響起铡原,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤偷厦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后燕刻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體只泼,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年卵洗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了请唱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,629評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡过蹂,死狀恐怖十绑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酷勺,我是刑警寧澤本橙,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站鸥印,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坦报。R本人自食惡果不足惜库说,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望片择。 院中可真熱鬧潜的,春花似錦、人聲如沸字管。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘲叔。三九已至亡呵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間硫戈,已是汗流浹背锰什。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丁逝,地道東北人汁胆。 一個(gè)月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像霜幼,于是被迫代替她去往敵國和親嫩码。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,499評論 2 348

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