深入理解JVM的內(nèi)存區(qū)域劃分

作者Java后端進階 轉(zhuǎn)載請注明出處

公眾號「Java后端進階

一、首先我們先熟悉一下JVM

1. 什么是JVM?

JVM是Java Virtual Machine(Java虛擬機)的縮寫箱舞,JVM是一種用于計算設(shè)備的規(guī)范怜浅,它是一個虛構(gòu)出來的計算機袁波,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的人断。Java虛擬機包括一套字節(jié)碼指令集、一組寄存器踪蹬、一個棧胞此、一個垃圾回收堆和一個存儲方法域。 JVM屏蔽了與具體操作系統(tǒng)平臺相關(guān)的信息跃捣,使Java程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行漱牵。JVM在執(zhí)行字節(jié)碼時,實際上最終還是把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行疚漆。

Java語言的一個非常重要的特點就是與平臺的無關(guān)性酣胀。而使用Java虛擬機是實現(xiàn)這一特點的關(guān)鍵。一般的高級語言如果要在不同的平臺上運行愿卸,至少需要編譯成不同的目標代碼灵临。而引入Java語言虛擬機后趴荸,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關(guān)的信息宦焦,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼)发钝,就可以在多種平臺上不加修改地運行精堕。Java虛擬機在執(zhí)行字節(jié)碼時孵淘,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行。這就是Java的能夠“一次編譯歹篓,到處運行”的原因瘫证。

2. JRE/JDK/JVM是什么關(guān)系揉阎?

JRE(JavaRuntimeEnvironment,Java運行環(huán)境)背捌,也就是Java平臺毙籽。所有的Java 程序都要在JRE下才能運行。普通用戶只需要運行已開發(fā)好的java程序毡庆,安裝JRE即可坑赡。

JDK(Java Development Kit)是程序開發(fā)者用來來編譯、調(diào)試java程序用的開發(fā)工具包么抗。JDK的工具也是Java程序毅否,也需要JRE才能運行。為了保持JDK的獨立性和完整性蝇刀,在JDK的安裝過程中搀突,JRE也是 安裝的一部分。所以熊泵,在JDK的安裝目錄下有一個名為jre的目錄仰迁,用于存放JRE文件。

JVM(JavaVirtualMachine顽分,Java虛擬機)是JRE的一部分徐许。它是一個虛構(gòu)出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的卒蘸。JVM有自己完善的硬件架構(gòu)雌隅,如處理器、堆棧缸沃、寄存器等恰起,還具有相應(yīng)的指令系統(tǒng)。Java語言最重要的特點就是跨平臺運行趾牧。使用JVM就是為了支持與操作系統(tǒng)無關(guān)检盼,實現(xiàn)跨平臺。

二翘单、Java程序的基本執(zhí)行原理:

1.執(zhí)行原理說明:首先java源代碼文件(.java后綴)會被java編譯器編譯為字節(jié)碼文件(.class文件)吨枉,然后由JVM中類加載器(Class Loader)加載各個類的.class文件,加載完成之后哄芜,交給執(zhí)行引擎執(zhí)行貌亭。在整個執(zhí)行過程中,

JVM會用一段空間來存儲程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息认臊,這段空間一般被稱為運行時數(shù)據(jù)區(qū)(Runtime Data Area),也就是我們常說的JVM內(nèi)存圃庭。因此,在java中我們常說內(nèi)存管理就是針對這段空間的管理。

2.原理圖:

二剧腻、了解了java程序的執(zhí)行原理拘央,下面我們就要針對于運行時數(shù)據(jù)區(qū)進行深入理解;

運行時數(shù)據(jù)區(qū)就是我們平常所說的JVM內(nèi)存恕酸,首先我們先了解一下JVM內(nèi)存區(qū)域的劃分:

1.內(nèi)存區(qū)域:可以劃分為線程共享和非線程共享堪滨,一般垃圾回收機制gc方法發(fā)生在線程共享的區(qū)域(大部分發(fā)生在Heap上)的原因。

1.Method Area(Non-heap)(方法區(qū))-----線程共享

2.Heap(堆)----線程共享

3.Program counter register(程序計數(shù)器)----非線程共享

4.VM Stack (虛擬機棧)? ----非線程共享

5.Nativa Method Stack (本地方法棧)----非線程共享

2.Jvm的運行區(qū)域

3.為什么分為線程共享和非線程共享的呢蕊温?袱箱??

我們明白了java程序的執(zhí)行原理义矛,我們再談jvm发笔;

JVM初始運行的時候都會分配好Method Area(方法區(qū))和Heap(堆),而JVM 每遇到一個線程凉翻,就為其分配一個Program Counter Register(程序計數(shù)器),VM Stack(虛擬機棧)和Native Method Stack?(本地方法棧)了讨,當線程終止時,三者(虛擬機棧制轰,本地方法棧和程序計數(shù)器)所占用的內(nèi)存空間也會被釋放掉前计。這也是為什么把內(nèi)存區(qū)域分為線程共享和非線程共享的原因,非線程共享的那三個區(qū)域的生命周期與所屬線程相同垃杖,而線程共享的區(qū)域與JAVA程序運行的生命周期相同男杈,所以這也是系統(tǒng)垃圾回收的場所只發(fā)生在線程共享的區(qū)域(實際上對大部分虛擬機來說知發(fā)生在Heap上)的原因。

1.虛擬機棧:

Java棧也稱作虛擬機棧(Java Vitual Machine Stack)调俘,也就是我們常常所說的棧伶棒,跟C語言的數(shù)據(jù)段中的棧類似。事實上彩库,Java棧是Java方法執(zhí)行的內(nèi)存模型肤无。為什么這么說呢?下面就來解釋一下其中的原因骇钦。

Java棧中存放的是一個個的棧幀宛渐,每個棧幀對應(yīng)一個被調(diào)用的方法,在棧幀中包括局部變量表(Local Variables)司忱、操作數(shù)棧(Operand Stack)皇忿、指向當前方法所屬的類的運行時常量池(運行時常量池的概念在方法區(qū)部分會談到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些額外的附加信息坦仍。當線程執(zhí)行一個方法時,就會隨之創(chuàng)建一個對應(yīng)的棧幀叨襟,并將建立的棧幀壓棧繁扎。當方法執(zhí)行完畢之后,便會將棧幀出棧。

因此可知梳玫,線程當前執(zhí)行的方法所對應(yīng)的棧幀必定位于Java棧的頂部爹梁。講到這里,大家就應(yīng)該會明白為什么 在 使用 遞歸方法的時候容易導(dǎo)致棧內(nèi)存溢出的現(xiàn)象了以及為什么棧區(qū)的空間不用程序員去管理了(當然在Java中提澎,程序員基本不用關(guān)系到內(nèi)存分配和釋放的事情姚垃,因為Java有自己的垃圾回收機制),這部分空間的分配和釋放都是由系統(tǒng)自動實施的盼忌。對于所有的程序設(shè)計語言來說积糯,棧這部分空間對程序員來說是不透明的。下圖表示了一個Java棧的模型:

局部變量表谦纱,顧名思義看成,想必不用解釋大家應(yīng)該明白它的作用了吧。就是用來存儲方法中的局部變量(包括在方法中聲明的非靜態(tài)變量以及函數(shù)形參)跨嘉。對于基本數(shù)據(jù)類型的變量川慌,則直接存儲它的值,對于引用類型的變量祠乃,則存的是指向?qū)ο蟮囊妹沃亍>植孔兞勘淼拇笮≡诰幾g器就可以確定其大小了,因此在程序執(zhí)行期間局部變量表的大小是不會改變的亮瓷。

操作數(shù)棧琴拧,想必學過數(shù)據(jù)結(jié)構(gòu)中的棧的朋友對表達式求值問題不會陌生,棧最典型的一個應(yīng)用就是用來對表達式求值寺庄。想想一個線程執(zhí)行方法的過程中艾蓝,實際上就是不斷執(zhí)行語句的過程,而歸根到底就是進行計算的過程斗塘。因此可以這么說赢织,程序中的所有計算過程都是在借助于操作數(shù)棧來完成的。

指向運行時常量池的引用馍盟,因為在方法執(zhí)行的過程中有可能需要用到類中的常量于置,所以必須要有一個引用指向運行時常量。

方法返回地址贞岭,當一個方法執(zhí)行完畢之后八毯,要返回之前調(diào)用它的地方,因此在棧幀中必須保存一個方法返回地址瞄桨。

由于每個線程正在執(zhí)行的方法可能不同话速,因此每個線程都會有一個自己的Java棧,互不干擾芯侥。

2.堆

在C語言中泊交,堆這部分空間是唯一一個程序員可以管理的內(nèi)存區(qū)域乳讥。程序員可以通過malloc函數(shù)和free函數(shù)在堆上申請和釋放空間。那么在Java中是怎么樣的呢廓俭?

Java中的堆是用來存儲對象本身的以及數(shù)組(當然云石,數(shù)組引用是存放在Java棧中的)。只不過和C語言中的不同研乒,在Java中汹忠,程序員基本不用去關(guān)心空間釋放的問題,Java的垃圾回收機制會自動進行處理雹熬。因此這部分空間也是Java垃圾收集器管理的主要區(qū)域宽菜。另外,堆是被所有線程共享的橄唬,在JVM中只有一個堆赋焕。

3.方法區(qū)

方法區(qū)在JVM中也是一個非常重要的區(qū)域,它與堆一樣仰楚,是被線程共享的區(qū)域隆判。在方法區(qū)中,存儲了每個類的信息(包括類的名稱僧界、方法信息侨嘀、字段信息)、靜態(tài)變量捂襟、常量以及編譯器編譯后的代碼等咬腕。

在Class文件中除了類的字段、方法葬荷、接口等描述信息外涨共,還有一項信息是常量池,用來存儲編譯期間生成的字面量和符號引用宠漩。

在方法區(qū)中有一個非常重要的部分就是運行時常量池举反,它是每一個類或接口的常量池的運行時表示形式,在類和接口被加載到JVM后扒吁,對應(yīng)的運行時常量池就被創(chuàng)建出來火鼻。當然并非Class文件常量池中的內(nèi)容才能進入運行時常量池,在運行期間也可將新的常量放入運行時常量池中雕崩,比如String的intern方法魁索。

在JVM規(guī)范中,沒有強制要求方法區(qū)必須實現(xiàn)垃圾回收盼铁。很多人習慣將方法區(qū)稱為“永久代”粗蔚,是因為HotSpot虛擬機以永久代來實現(xiàn)方法區(qū),從而JVM的垃圾收集器可以像管理堆區(qū)一樣管理這部分區(qū)域饶火,從而不需要專門為這部分設(shè)計垃圾回收機制支鸡。不過自從JDK7之后冬念,Hotspot虛擬機便將運行時常量池從永久代移除了趁窃。

4.程序計數(shù)器

程序計數(shù)器(Program Counter Register)牧挣,也有稱作為PC寄存器。想必學過匯編語言的朋友對程序計數(shù)器這個概念并不陌生醒陆,在匯編語言中瀑构,程序計數(shù)器是指CPU中的寄存器,它保存的是程序當前執(zhí)行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址)刨摩,當CPU需要執(zhí)行指令時寺晌,需要從程序計數(shù)器中得到當前需要執(zhí)行的指令所在存儲單元的地址,然后根據(jù)得到的地址獲取到指令澡刹,在得到指令之后呻征,程序計數(shù)器便自動加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址,如此循環(huán)罢浇,直至執(zhí)行完所有的指令陆赋。

雖然JVM中的程序計數(shù)器并不像匯編語言中的程序計數(shù)器一樣是物理概念上的CPU寄存器,但是JVM中的程序計數(shù)器的功能跟匯編語言中的程序計數(shù)器的功能在邏輯上是等同的嚷闭,也就是說是用來指示 執(zhí)行哪條指令的攒岛。

由于在JVM中,多線程是通過線程輪流切換來獲得CPU執(zhí)行時間的胞锰,因此灾锯,在任一具體時刻,一個CPU的內(nèi)核只會執(zhí)行一條線程中的指令嗅榕,因此顺饮,為了能夠使得每個線程都在線程切換后能夠恢復(fù)在切換之前的程序執(zhí)行位置,每個線程都需要有自己獨立的程序計數(shù)器凌那,并且不能互相被干擾兼雄,否則就會影響到程序的正常執(zhí)行次序。因此案怯,可以這么說君旦,程序計數(shù)器是每個線程所私有的。

在JVM規(guī)范中規(guī)定嘲碱,如果線程執(zhí)行的是非native方法金砍,則程序計數(shù)器中保存的是當前需要執(zhí)行的指令的地址;如果線程執(zhí)行的是native方法麦锯,則程序計數(shù)器中的值是undefined恕稠。

由于程序計數(shù)器中存儲的數(shù)據(jù)所占空間的大小不會隨程序的執(zhí)行而發(fā)生改變,因此扶欣,對于程序計數(shù)器是不會發(fā)生內(nèi)存溢出現(xiàn)象(OutOfMemory)的鹅巍。

5.本地方法棧

本地方法棧與Java棧的作用和原理非常相似千扶。區(qū)別只不過是Java棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的骆捧。在JVM規(guī)范中澎羞,并沒有對本地方法棧的具體實現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強制規(guī)定,虛擬機可以自由實現(xiàn)它敛苇。在HotSopt虛擬機中直接就把本地方法棧和Java棧合二為一妆绞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市枫攀,隨后出現(xiàn)的幾起案子括饶,更是在濱河造成了極大的恐慌,老刑警劉巖来涨,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件图焰,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹦掐,警方通過查閱死者的電腦和手機技羔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笤闯,“玉大人堕阔,你說我怎么就攤上這事】盼叮” “怎么了超陆?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浦马。 經(jīng)常有香客問我时呀,道長,這世上最難降的妖魔是什么晶默? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任谨娜,我火速辦了婚禮,結(jié)果婚禮上磺陡,老公的妹妹穿的比我還像新娘趴梢。我一直安慰自己媒佣,他們只是感情好藐鹤,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹂安,像睡著了一般蝴悉。 火紅的嫁衣襯著肌膚如雪彰阴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天拍冠,我揣著相機與錄音尿这,去河邊找鬼簇抵。 笑死,一個胖子當著我的面吹牛射众,可吹牛的內(nèi)容都是我干的碟摆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼责球,長吁一口氣:“原來是場噩夢啊……” “哼焦履!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起雏逾,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎郑临,沒想到半個月后栖博,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡厢洞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年仇让,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躺翻。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡丧叽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出公你,到底是詐尸還是另有隱情踊淳,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布陕靠,位于F島的核電站迂尝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剪芥。R本人自食惡果不足惜垄开,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望税肪。 院中可真熱鬧溉躲,春花似錦、人聲如沸益兄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽偏塞。三九已至唱蒸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灸叼,已是汗流浹背神汹。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工庆捺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屁魏。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓滔以,卻偏偏與公主長得像,于是被迫代替她去往敵國和親氓拼。 傳聞我的和親對象是個殘疾皇子你画,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348