深入理解java內(nèi)存結(jié)構(gòu)

概述

對(duì)于從事C、C++程序開(kāi)發(fā)的開(kāi)發(fā)人員來(lái)說(shuō)饥悴,在內(nèi)存管理領(lǐng)域,他們既是擁有最高權(quán)力的“皇帝”又是從事最基礎(chǔ)工作的“勞動(dòng)人民”-------既擁有每一 個(gè)對(duì)象的 “所有權(quán)",又肩負(fù)著每一個(gè)對(duì)象生 命開(kāi)始到終結(jié)的維護(hù)責(zé)任。

對(duì)于Java程序員來(lái)說(shuō)峡蟋,在虛擬機(jī)自動(dòng)內(nèi)存管理機(jī)制的幫助下晶通,不再需要為每一個(gè)new操作去寫(xiě)配對(duì)的delete/free代碼璃氢,不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問(wèn)題,由虛擬機(jī)管理內(nèi)存這一切看起來(lái)都很美好狮辽。不過(guò)一也,也正是因?yàn)镴ava程序員把內(nèi)存控制的權(quán)力交給了JVM巢寡,一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問(wèn)題,如果不了解虛擬機(jī)是怎樣使用內(nèi)存的椰苟,那么排查錯(cuò)誤將會(huì)成為一項(xiàng)異常艱難的工作抑月。

接下來(lái)我將從概念上介紹Java虛擬機(jī)內(nèi)存的各個(gè)區(qū)城,講述這些區(qū)域的作用舆蝴、服務(wù)對(duì)象以及其中可能產(chǎn)生的問(wèn)題.

運(yùn)行時(shí)數(shù)據(jù)區(qū)域

Java虛擬機(jī)在執(zhí)行Java程序的過(guò)程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域谦絮。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷(xiāo)毀的時(shí)間须误,有的區(qū)域隨著虛擬機(jī)進(jìn)程的啟動(dòng)而存在挨稿,有些區(qū)域則依賴(lài)用戶(hù)線(xiàn)程的啟動(dòng)和結(jié)束而建立和銷(xiāo)毀。根據(jù)(Java 虛報(bào)機(jī)規(guī)范SE 7版)》的規(guī)定京痢,Java 虛擬機(jī)所管理的內(nèi)存將會(huì)包括以下幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)城奶甘,如圖所示。


java虛擬機(jī)棧

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

經(jīng)常有人把Java內(nèi)存區(qū)分為堆(Heap)和棧(Stack),這種分法比較的粗糙,java的內(nèi)存遠(yuǎn)比這要復(fù)雜,這種劃分方式的流行只說(shuō)明大多數(shù)程序員最關(guān)注的,與對(duì)象分配關(guān)系最密切的是這兩塊內(nèi)存.,對(duì)于堆我會(huì)在后面會(huì)專(zhuān)門(mén)講述,而棧就是虛擬機(jī)椔疲或者說(shuō)是虛擬機(jī)棧中的局部變量表部分.

局部變量表:

在這里我會(huì)簡(jiǎn)單介紹局部變量表存放的內(nèi)容稍后會(huì)有文章專(zhuān)門(mén)介紹java運(yùn)行時(shí)棧幀結(jié)構(gòu).

局部變量表存放了編譯器可知的各種數(shù)據(jù)類(lèi)型(boolean,byte,char,short,int,float,long,double),對(duì)象引用(reference類(lèi)型)和returnAddress類(lèi)型(指向一條字節(jié)碼指令的地址)

在Java虛擬機(jī)規(guī)范中种蘸,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常狀況:如果線(xiàn)程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOveFlowError異常;如果虛擬機(jī)椂ち蓿可以動(dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,只不過(guò)Java虛擬機(jī)規(guī)范中也允許固定長(zhǎng)度的虛擬機(jī)棧)携茂,如果擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存你踩,就會(huì)拋出OutOfMemoryEror異常。

本地方法棧

本地方法棧 (Native Method Stack與虛擬機(jī)棧的作用是非常相似的讳苦, 它的區(qū)別不過(guò)是虛報(bào)機(jī)棧是為虛報(bào)機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)带膜,本地方法棧是為虛報(bào)機(jī)使用到的Native方法服務(wù)的。與虛擬機(jī)棧一樣鸳谜,本地方準(zhǔn)棧區(qū)域也會(huì)拋出StackOverflowError 和OutOfMemoryError異常膝藕。

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

程序計(jì)數(shù)器(Pregram Counter Register是塊較小的內(nèi)存空間, 它可以看作是當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼的行號(hào)指示器咐扭。在虛擬機(jī)的概念模型里,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令芭挽,分支、循環(huán)蝗肪、跳轉(zhuǎn)袜爪、異常處理、線(xiàn)程恢復(fù)等基礎(chǔ)功能都需要依賴(lài)這個(gè)計(jì)數(shù)器來(lái)完成穗慕。

由于Java虛擬機(jī)的多線(xiàn)程是通過(guò)線(xiàn)程輪流切換并分配處理器執(zhí)行時(shí)間的方式來(lái)實(shí)現(xiàn)的饿敲,在任何一個(gè)確定的時(shí)刻, 一個(gè)處理器 (對(duì)于多核處理器來(lái)說(shuō)是一 個(gè)內(nèi)核)都只會(huì)執(zhí)行 一條線(xiàn)程中的指令逛绵。因此怀各,為了線(xiàn)程切換后能恢復(fù)到正確的執(zhí)行位置倔韭,每條線(xiàn)程都需要有一一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線(xiàn)程之間計(jì)數(shù)器互不影響瓢对,獨(dú)立存儲(chǔ)寿酌,我們稱(chēng)這類(lèi)內(nèi)存區(qū)域?yàn)椤熬€(xiàn)程私有”的內(nèi)存。

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

java堆

對(duì)于大多數(shù)應(yīng)用來(lái)說(shuō)秧荆,Java堆 (Java Heap)是Java虛擬機(jī)所管理的內(nèi)存中最大的內(nèi)存塊。Java堆是被所有線(xiàn)程共享的一塊內(nèi)存區(qū)域埃仪, 在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建乙濒。此內(nèi)存區(qū)域的唯一作用就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存卵蛉。這一點(diǎn)在Java康擬機(jī)規(guī)范中的描述是:所有的對(duì)象實(shí)例以及數(shù)組都要在堆上分配颁股,但是隨著JIT編譯器的發(fā)展與逃逸分析技術(shù)逐斷成熟,棧上分配傻丝、標(biāo)量替換優(yōu)化技術(shù)將會(huì)導(dǎo)致一些微妙的變化發(fā)生甘有, 所有的對(duì)象都分配在堆上也漸漸變得不是那么“絕對(duì)”了。

Java堆是垃圾收集器管理的主要區(qū)域葡缰,因此很多時(shí)候也被稱(chēng)做“GC堆”亏掀。從內(nèi)存回收的角度來(lái)看,由于收集器基本都采用分代收集算法泛释,所以Java堆中還可以細(xì)分為:新生代和老年代:再細(xì)致一點(diǎn)的有Eden空間幌氮、From Survivor空間、To Survivor空間等胁澳。從內(nèi)存分配的角度來(lái)看,線(xiàn)程共享的Java堆中可能劃分出多個(gè)線(xiàn)程私有的分配緩沖區(qū)(Thread Local Alocation Buffer, TLAB).不過(guò)無(wú)論如何劃分米者,都與存放內(nèi)容無(wú)關(guān)韭畸,無(wú)論哪個(gè)區(qū)域,存儲(chǔ)的都仍然是對(duì)象實(shí)例蔓搞,進(jìn)步劃分的目的是為了更好地回收內(nèi)存胰丁,或者更快地分配內(nèi)存。

根據(jù)Java虛擬機(jī)規(guī)范的規(guī)定喂分,Java 堆可以處于物理上不連續(xù)的內(nèi)存空間中锦庸,只要邏輯上是連續(xù)的即可,就像我們的磁盤(pán)空間一樣蒲祈。在實(shí)現(xiàn)時(shí)甘萧,既可以實(shí)現(xiàn)成固定大小的萝嘁,也可以是可擴(kuò)展的,不過(guò)當(dāng)前主流的虛擬機(jī)都是按照可擴(kuò)展來(lái)實(shí)現(xiàn)的(通過(guò)-Xmx和-Xms控制).如果在堆中設(shè)有內(nèi)存完成實(shí)例分配扬卷,并且堆也無(wú)法再擴(kuò)展時(shí)牙言,將會(huì)拋出OutOfMemoryError異常.

方法區(qū)

方法區(qū)(Method Area)與Java堆一樣,是各個(gè)線(xiàn)程共享的內(nèi)存區(qū)城怪得,它用于存儲(chǔ)已被虛擬機(jī)加載的類(lèi)信息咱枉、常量、靜態(tài)變量徒恋、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)蚕断。

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

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

直接內(nèi)存

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

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

總結(jié)

以上是對(duì)java'運(yùn)行時(shí)數(shù)據(jù)區(qū)的詳細(xì)介紹.如果有疑問(wèn)可以與我聯(lián)系,相互討論學(xué)習(xí).

在了解了java運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu),接下來(lái)我會(huì)介紹java虛擬機(jī)內(nèi)存中的其他細(xì)節(jié),如對(duì)象是如何創(chuàng)建的,對(duì)象是如何布局的以及如何訪(fǎng)問(wèn)的等.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市指黎,隨后出現(xiàn)的幾起案子朋凉,更是在濱河造成了極大的恐慌,老刑警劉巖醋安,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杂彭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吓揪,警方通過(guò)查閱死者的電腦和手機(jī)亲怠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柠辞,“玉大人团秽,你說(shuō)我怎么就攤上這事。” “怎么了习勤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵踪栋,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我姻报,道長(zhǎng)己英,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任吴旋,我火速辦了婚禮损肛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荣瑟。我一直安慰自己治拿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布笆焰。 她就那樣靜靜地躺著劫谅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嚷掠。 梳的紋絲不亂的頭發(fā)上捏检,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音不皆,去河邊找鬼贯城。 笑死,一個(gè)胖子當(dāng)著我的面吹牛霹娄,可吹牛的內(nèi)容都是我干的能犯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼犬耻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼踩晶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起枕磁,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渡蜻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后计济,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晴楔,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年峭咒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纪岁。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凑队,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漩氨,我是刑警寧澤西壮,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站叫惊,受9級(jí)特大地震影響款青,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霍狰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一抡草、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蔗坯,春花似錦康震、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至绘梦,卻和暖如春橘忱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卸奉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工钝诚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人择卦。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓敲长,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秉继。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祈噪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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