筆記之JVM內(nèi)存區(qū)域劃分

Java程序是交由JVM執(zhí)行的,在討論JVM內(nèi)存區(qū)域劃分之前秋冰,先來看一下Java程序具體執(zhí)行的過程仲义,如圖:

281630330728961.jpg

如上圖所示,首先Java源代碼文件(.java后綴)會被Java編譯器編譯為字節(jié)碼文件(.class后綴)剑勾,然后由JVM中的類加載器加載各個類的字節(jié)碼文件埃撵,加載完畢之后,交由JVM執(zhí)行引擎執(zhí)行虽另。在整個程序執(zhí)行過程中暂刘,JVM會用一段空間來存儲程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間一般被稱作為Runtime Data Area(運行時數(shù)據(jù)區(qū))捂刺,也就是我們常說的JVM內(nèi)存谣拣。因此,在Java中我們常常說到的內(nèi)存管理就是針對這段空間進行管理(如何分配和回收內(nèi)存空間)族展。
我們主要了解兩部分:

運行時數(shù)據(jù)區(qū)包括哪幾部分

根據(jù)《Java虛擬機規(guī)范》的規(guī)定森缠,運行時數(shù)據(jù)區(qū)通常包括這幾個部分:程序計數(shù)器(Program Counter Register)、Java棧(VM Stack)仪缸、本地方法棧(Native Method Stack)贵涵、方法區(qū)(Method Area)、堆(Heap)恰画,如圖:

281726404166686.jpg

運行時數(shù)據(jù)區(qū)的每部分到底存儲了哪些數(shù)據(jù)

程序計數(shù)器

程序計數(shù)器(Program Counter Register)宾茂,也有稱作為PC寄存器。在匯編語言中锣尉,程序計數(shù)器是指CPU中的寄存器刻炒,它保存的是程序當前執(zhí)行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當CPU需要執(zhí)行指令時自沧,需要從程序計數(shù)器中得到當前需要執(zhí)行的指令所在存儲單元的地址坟奥,然后根據(jù)得到的地址獲取到指令,在得到指令之后拇厢,程序計數(shù)器便自動加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址爱谁,如此循環(huán),直至執(zhí)行完所有的指令孝偎。

雖然JVM中的程序計數(shù)器并不像匯編語言中的程序計數(shù)器一樣是物理概念上的CPU寄存器访敌,但是JVM中的程序計數(shù)器的功能跟匯編語言中的程序計數(shù)器的功能在邏輯上是等同的,也就是說是用來指示 執(zhí)行哪條指令的衣盾∷峦可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器爷抓,也可以理解為下一條將要執(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)的。

Java棧

Java棧也稱作虛擬機棧(Java Vitual Machine Stack)嫡丙,虛擬機棧描述的是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棧的頂部。

棧幀( Frame)是用來存儲數(shù)據(jù)和部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu),同時也被用來處理動態(tài)鏈接 (Dynamic Linking)蒋腮、 方法返回值和異常分派( Dispatch Exception)淘捡。
棧幀隨著方法調(diào)用而創(chuàng)建,隨著方法結(jié)束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內(nèi)未被捕獲的異常)都算作方法結(jié)束池摧。
棧幀的存儲空間分配在 Java 虛擬機棧之中案淋,每一個棧幀都有自己的局部變量表( Local Variables)、操作數(shù)棧( OperandStack)和指向當前方法所屬的類的運行時常量池的引用险绘。

下圖表示了一個Java棧的模型:

291429030562182.jpg
  • 局部變量表,就是用來存儲方法中的局部變量(包括在方法中聲明的非靜態(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棧,互不干擾顶别。當線程執(zhí)行一個方法時谷徙,就會隨之創(chuàng)建一個對應(yīng)的棧幀,并將建立的棧幀壓棧驯绎。

本地方法棧

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

Java中的堆是用來存儲對象本身的以及數(shù)組(當然脾歧,數(shù)組引用是存放在Java棧中的)甲捏。在Java中,程序員基本不用去關(guān)心空間釋放的問題鞭执,Java的垃圾回收機制會自動進行處理司顿。因此這部分空間也是Java垃圾收集器管理的主要區(qū)域。另外兄纺,堆是被所有線程共享的大溜,在JVM中只有一個堆。

在 Java 虛擬機中估脆,堆( Heap)是可供各條線程共享的運行時內(nèi)存區(qū)域钦奋,也是供所有類實例和數(shù)組對象分配內(nèi)存的區(qū)域。

  • 在虛擬機啟動的時候就被創(chuàng)建

  • 是所有線程共享的內(nèi)存區(qū)域

  • 存儲了被自動內(nèi)存管理系統(tǒng)所管理的各種對象

這些受管理的對象無需疙赠,也無法顯式地被銷毀

自動內(nèi)存管理系統(tǒng):Automatic StorageManagement System付材,也即是常說的"Garbage Collector(垃圾收集器)"
  • Java 堆的容量可以是固定大小的,也可以隨著程序執(zhí)行的需求動態(tài)擴展圃阳,并在不需要過多空間時自動收縮

  • Java 堆所使用的內(nèi)存不需要保證是連續(xù)的

  • 如果實際所需的堆超過了自動內(nèi)存管理系統(tǒng)能提供的最大容量厌衔,那 Java 虛擬機將會拋出一個OutOfMemoryError 異常

  • 實現(xiàn)者應(yīng)當提供給程序員或者最終用戶調(diào)節(jié) Java 堆初始容量的手段

  • 所有的對象實例以及數(shù)組都要在堆上分配

方法區(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虛擬機便將運行時常量池從永久代移除了。

3028880-08bb31e053ae7a99.png
直接內(nèi)存(Direct Memory)

直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分歌豺,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域芯丧,它直接從操作系統(tǒng)中分配,因此不受Java堆大小的限制世曾,但是會受到本機總內(nèi)存的大小及處理器尋址空間的限制,因此它也可能導致OutOfMemoryError異常出現(xiàn)谴咸。在JDK1.4中新引入了NIO機制轮听,它是一種基于通道與緩沖區(qū)的新I/O方式,可以直接從操作系統(tǒng)中分配直接內(nèi)存岭佳,即在堆外分配內(nèi)存血巍,這樣能在一些場景中提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)珊随。

此處的直接內(nèi)存并不是由JVM管理的內(nèi)存述寡。他是利用本地方法庫直接在java堆之外申請的內(nèi)存區(qū)域。比如NIO中的DirectByteBuffer就是操作直接內(nèi)存的叶洞。

直接內(nèi)存的好處就是避免了在java堆和native堆直接同步數(shù)據(jù)的步驟鲫凶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市衩辟,隨后出現(xiàn)的幾起案子螟炫,更是在濱河造成了極大的恐慌,老刑警劉巖艺晴,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昼钻,死亡現(xiàn)場離奇詭異,居然都是意外死亡封寞,警方通過查閱死者的電腦和手機然评,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狈究,“玉大人碗淌,你說我怎么就攤上這事。” “怎么了贯莺?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵风喇,是天一觀的道長。 經(jīng)常有香客問我缕探,道長魂莫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任爹耗,我火速辦了婚禮耙考,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘潭兽。我一直安慰自己倦始,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布山卦。 她就那樣靜靜地躺著鞋邑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪账蓉。 梳的紋絲不亂的頭發(fā)上枚碗,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音铸本,去河邊找鬼肮雨。 笑死,一個胖子當著我的面吹牛箱玷,可吹牛的內(nèi)容都是我干的怨规。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼锡足,長吁一口氣:“原來是場噩夢啊……” “哼波丰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起舶得,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呀舔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扩灯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媚赖,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年珠插,在試婚紗的時候發(fā)現(xiàn)自己被綠了惧磺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡捻撑,死狀恐怖磨隘,靈堂內(nèi)的尸體忽然破棺而出缤底,到底是詐尸還是另有隱情,我是刑警寧澤番捂,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布个唧,位于F島的核電站,受9級特大地震影響设预,放射性物質(zhì)發(fā)生泄漏徙歼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一鳖枕、第九天 我趴在偏房一處隱蔽的房頂上張望魄梯。 院中可真熱鬧,春花似錦宾符、人聲如沸酿秸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辣苏。三九已至,卻和暖如春哄褒,著一層夾襖步出監(jiān)牢的瞬間考润,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工读处, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唱矛。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓罚舱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绎谦。 傳聞我的和親對象是個殘疾皇子管闷,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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