JVM-內(nèi)存區(qū)域劃分

Java源代碼文件(.java)會(huì)被Java編譯器編譯為字節(jié)碼文件(.class)网杆,然后由JVM中的類加載器加載各個(gè)類的字節(jié)碼文件哎甲,加載完畢之后著榴,交由JVM執(zhí)行引擎執(zhí)行。JVM在執(zhí)行Java程序的過程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域憎乙。JVM會(huì)用一段空間來存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息票罐,這段空間一般被稱作為Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū)),也就是我們常說的JVM內(nèi)存寨闹。

我們可以把上圖的“運(yùn)行時(shí)數(shù)據(jù)區(qū)”分為線程私有和共享數(shù)據(jù)區(qū)兩大類胶坠。其中線程私有的數(shù)據(jù)區(qū)包含程序計(jì)數(shù)器、虛擬機(jī)棧繁堡、本地方法區(qū)沈善,所有線程共享的數(shù)據(jù)區(qū)包含Java堆、方法區(qū)椭蹄,在方法區(qū)內(nèi)有一個(gè)常量池闻牡。

線程私有

1.程序計(jì)數(shù)器:記錄正在執(zhí)行的虛擬機(jī)字節(jié)碼的地址

由于Java 虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的,在任何一個(gè)確定的時(shí)刻绳矩,一個(gè)處理器(對(duì)于多核處理器來說是一個(gè)內(nèi)核)只會(huì)執(zhí)行一條線程中的指令罩润。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置翼馆,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器割以,各條線程之間的計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)应媚。線程私有的严沥。如果正在執(zhí)行的是Natvie 方法,這個(gè)計(jì)數(shù)器值則空中姜。此內(nèi)存區(qū)域是唯一一個(gè)在Java 虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域消玄。

2.虛擬機(jī)棧:描述的是Java 方法執(zhí)行的內(nèi)存模型

它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame), 棧幀是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)丢胚,用于存儲(chǔ)局部變量表翩瓜、操作數(shù)棧、動(dòng)態(tài)鏈接携龟、方法出口等信息兔跌。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程锄列,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程它呀。

在Java虛擬機(jī)規(guī)范中髓窜,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常狀況:如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度丈探,將拋出StackOverflowError異常;如果虛擬機(jī)楄胱洌可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,只不過Java虛擬機(jī)規(guī)范中也允許固定長度的虛擬機(jī)棧)匿又,如果擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存方灾,就會(huì)拋出OutOfMemoryError異常。

  • 局部變量表
    局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean碌更、byte裕偿、char、short痛单、int嘿棘、float、long旭绒、double)鸟妙、對(duì)象引用(reference類型,它不等同于對(duì)象本身挥吵,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔樦馗福部赡苁侵赶蛞粋€(gè)代表對(duì)象的句柄或其他與此對(duì)象相關(guān)的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)。

    其中64位長度的long和double類型的數(shù)據(jù)會(huì)占用2個(gè)局部變量空間(Slot)忽匈,其余的數(shù)據(jù)類型只占用1個(gè)房午。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí)丹允,這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的郭厌,在方法運(yùn)行期間不會(huì)改變局部變量表的大小。

  • 操作數(shù)棧
    Java虛擬機(jī)的解釋執(zhí)行引擎被稱為"基于棧的執(zhí)行引擎"雕蔽,其中所指的棧就是指-操作數(shù)棧折柠。

    和局部變量區(qū)一樣,操作數(shù)棧也是被組織成一個(gè)以字長為單位的數(shù)組萎羔。但是和前者不同的是液走,它不是通過索引來訪問,而是通過標(biāo)準(zhǔn)的棧操作—壓棧和出椉窒荩—來訪問的缘眶。比如,如果某個(gè)指令把一個(gè)值壓入到操作數(shù)棧中髓废,稍后另一個(gè)指令就可以彈出這個(gè)值來使用巷懈。

    虛擬機(jī)把操作數(shù)棧作為它的工作區(qū)——大多數(shù)指令都要從這里彈出數(shù)據(jù),執(zhí)行運(yùn)算,然后把結(jié)果壓回操作數(shù)棧

  • 動(dòng)態(tài)鏈接
    Class文件中的常量持中存有大量的符號(hào)引用。字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用作為參數(shù)慌洪。這些符號(hào)引用一部分在類的加載階段(解析)或第一次使用的時(shí)候就轉(zhuǎn)化為了直接引用(指向數(shù)據(jù)所存地址的指針或句柄等)顶燕,這種轉(zhuǎn)化稱為靜態(tài)鏈接凑保。而相反的,另一部分在運(yùn)行期間轉(zhuǎn)化為直接引用涌攻,就稱為動(dòng)態(tài)鏈接欧引。

3.本地方法棧:為虛擬機(jī)使用到的Native方法提供內(nèi)存空間

在虛擬機(jī)規(guī)范中對(duì)本地方法棧中方法使用的語言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定恳谎,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它芝此。甚至有的虛擬機(jī)(譬如Sun HotSpot虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一。與虛擬機(jī)棧一樣因痛,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常婚苹。

線程公有

1.堆:存放對(duì)象實(shí)例

堆是Java 虛擬機(jī)所管理的內(nèi)存中最大的一塊。Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域鸵膏,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建膊升。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存谭企。在Java虛擬機(jī)規(guī)范中的描述是:所有的對(duì)象實(shí)例以及數(shù)組都要在堆上分配廓译,但是隨著JIT 編譯器的發(fā)展與逃逸分析技術(shù)的逐漸成熟,棧上分配赞咙、標(biāo)量替換優(yōu)化技術(shù)將會(huì)導(dǎo)致一些微妙的變化發(fā)生责循,所有的對(duì)象都分配在堆上也漸漸變得不是那么“絕對(duì)”了。堆是垃圾收集器管理的主要區(qū)域攀操。

根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定院仿,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可速和,就像我們的磁盤空間一樣歹垫。在實(shí)現(xiàn)時(shí),既可以實(shí)現(xiàn)成固定大小的颠放,也可以是可擴(kuò)展的排惨,不過當(dāng)前主流的虛擬機(jī)都是按照可擴(kuò)展來實(shí)現(xiàn)的(通過-Xmx和-Xms控制)。如果在堆中沒有內(nèi)存完成實(shí)例分配碰凶,并且堆也無法再擴(kuò)展時(shí)暮芭,將會(huì)拋出OutOfMemoryError異常。

2.方法區(qū):用于存儲(chǔ)已被虛擬機(jī)加載的類信息欲低、常量辕宏、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)

Java虛擬機(jī)規(guī)范對(duì)方法區(qū)的限制非常寬松砾莱,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展外瑞筐,還可以選擇不實(shí)現(xiàn)垃圾收集。相對(duì)而言腊瑟,垃圾收集行為在這個(gè)區(qū)域是比較少出現(xiàn)的聚假,但并非數(shù)據(jù)進(jìn)入了方法區(qū)就“永久”存在了块蚌。這區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和對(duì)類型的卸載,一般來說膘格,這個(gè)區(qū)域的回收“成績”比較難以令人滿意峭范,尤其是類型的卸載,條件相當(dāng)苛刻瘪贱,但是這部分區(qū)域的回收確實(shí)是必要的虎敦。

運(yùn)行時(shí)常量池:用于存放編譯期生成的各種字面量和符號(hào)引用

運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本政敢、字段、方法胚迫、接口等描述信息外喷户,還有一項(xiàng)信息是常量池,用于存放編譯期生成的各種字面量和符號(hào)引用访锻,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放褪尝。

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

運(yùn)行時(shí)常量池是方法區(qū)的一部分璃谨,自然受到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常鲤妥。

直接內(nèi)存

直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分佳吞,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域。但是這部分內(nèi)存也被頻繁地使用棉安,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)

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

直接內(nèi)存的分配不會(huì)受到Java堆大小的限制阱冶,但是,既然是內(nèi)存凳宙,肯定還是會(huì)受到本機(jī)總內(nèi)存(包括RAM以及SWAP區(qū)或者分頁文件)大小以及處理器尋址空間的限制熙揍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市氏涩,隨后出現(xiàn)的幾起案子届囚,更是在濱河造成了極大的恐慌有梆,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件意系,死亡現(xiàn)場(chǎng)離奇詭異泥耀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛔添,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門痰催,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迎瞧,你說我怎么就攤上這事夸溶。” “怎么了凶硅?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵缝裁,是天一觀的道長。 經(jīng)常有香客問我足绅,道長捷绑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任氢妈,我火速辦了婚禮粹污,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘首量。我一直安慰自己壮吩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布蕾总。 她就那樣靜靜地躺著粥航,像睡著了一般。 火紅的嫁衣襯著肌膚如雪生百。 梳的紋絲不亂的頭發(fā)上递雀,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蚀浆,去河邊找鬼缀程。 笑死,一個(gè)胖子當(dāng)著我的面吹牛市俊,可吹牛的內(nèi)容都是我干的杨凑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼摆昧,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼撩满!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤伺帘,失蹤者是張志新(化名)和其女友劉穎昭躺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伪嫁,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡领炫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了张咳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帝洪。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脚猾,靈堂內(nèi)的尸體忽然破棺而出葱峡,到底是詐尸還是另有隱情,我是刑警寧澤龙助,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布族沃,位于F島的核電站,受9級(jí)特大地震影響泌参,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜常空,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一沽一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漓糙,春花似錦铣缠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至醉鳖,卻和暖如春捡硅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盗棵。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工壮韭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纹因。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓喷屋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瞭恰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屯曹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355