java內(nèi)存管理和內(nèi)存模型

Java內(nèi)存模型是每個(gè)java程序員必須掌握理解的袍祖,這是Java的核心基礎(chǔ)疏叨,對(duì)我們編寫代碼特別是并發(fā)編程時(shí)有很大幫助斯议。由于Java程序是交由JVM執(zhí)行的莲兢,所以我們?cè)谡凧ava內(nèi)存區(qū)域劃分的時(shí)候事實(shí)上是指JVM內(nèi)存區(qū)域劃分。

首先偏序,我們回顧一下Java程序執(zhí)行流程:

如上圖所示页畦,首先Java源代碼文件(.java后綴)會(huì)被Java編譯器編譯為字節(jié)碼文件(.class后綴),然后由JVM中的類加載器加載各個(gè)類的字節(jié)碼文件研儒,加載完畢之后豫缨,交由JVM執(zhí)行引擎執(zhí)行。在整個(gè)程序執(zhí)行過程中端朵,JVM會(huì)用一段空間來存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息好芭,這段空間一般被稱作為Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū)),也就是我們常說的JVM內(nèi)存冲呢。因此舍败,在Java中我們常常說到的內(nèi)存管理就是針對(duì)這段空間進(jìn)行管理(如何分配和回收內(nèi)存空間)。

那么本篇文章主要是要分析Runtime Data Area(運(yùn)行時(shí)數(shù)據(jù)區(qū))的結(jié)構(gòu)碗硬。

  1. 運(yùn)行時(shí)數(shù)據(jù)區(qū)分為幾個(gè)部分瓤湘?

根據(jù) JVM 規(guī)范,JVM 內(nèi)存共分為虛擬機(jī)棧恩尾、堆、方法區(qū)挽懦、程序計(jì)數(shù)器翰意、本地方法棧五個(gè)部分。

名稱
特征
作用
配置參數(shù)
異常
程序計(jì)數(shù)器
占用內(nèi)存小信柿,線程私有冀偶,
生命周期與線程相同
大致為字節(jié)碼行號(hào)指示器


虛擬機(jī)棧
線程私有,生命周期與線程相同渔嚷,使用連續(xù)的內(nèi)存空間
Java 方法執(zhí)行的內(nèi)存模型进鸠,存儲(chǔ)局部變量表、操作棧形病、動(dòng)態(tài)鏈接客年、方法出口等信息
-Xss
StackOverflowError
OutOfMemoryError
java堆
線程共享霞幅,生命周期與虛擬機(jī)相同,可以不使用連續(xù)的內(nèi)存地址
保存對(duì)象實(shí)例量瓜,所有對(duì)象實(shí)例(包括數(shù)組)都要在堆上分配
-Xms
-Xsx
-Xmn
OutOfMemoryError
方法區(qū)
線程共享司恳,生命周期與虛擬機(jī)相同,可以不使用連續(xù)的內(nèi)存地址
存儲(chǔ)已被虛擬機(jī)加載的類信息绍傲、常量扔傅、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
-XX:PermSize:
16M
-XX:MaxPermSize
64M
OutOfMemoryError
運(yùn)行時(shí)常量池
方法區(qū)的一部分烫饼,具有動(dòng)態(tài)性
存放字面量及符號(hào)引用

1.1 方法區(qū)

方法區(qū)是java虛擬機(jī)規(guī)范去中定義的一種概念上的區(qū)域猎塞,具有什么功能,但并沒有規(guī)定這個(gè)區(qū)域到底應(yīng)該位于何處杠纵,因此對(duì)于實(shí)現(xiàn)者來說荠耽,如何來實(shí)際方法區(qū)是有著很大自由度的。

永生代是hotspot中的一個(gè)概念淡诗,其他jvm實(shí)現(xiàn)未必有骇塘,例如jrockit就沒這東西。java8之前韩容,hotspot使用在內(nèi)存中劃分出一塊區(qū)域來存儲(chǔ)類的元信息款违、類變量以及內(nèi)部字符串(interned string)等內(nèi)容,稱之為永生代群凶,把它作為方法區(qū)來使用插爹。

[JEP122][2]提議取消永生代,方法區(qū)作為概念上的區(qū)域仍然存在请梢。原先永生代中類的元信息會(huì)被放入本地內(nèi)存(元數(shù)據(jù)區(qū)赠尾,metaspace),將類的靜態(tài)變量和內(nèi)部字符串放入到j(luò)ava堆中毅弧。

為了弄清楚方法區(qū)那么需要解釋兩個(gè)名詞:永久代和元空間

PermGen(永久代)

絕大部分Java程序員應(yīng)該都見過“java.lang.OutOfMemoryError: PremGen space”異常气嫁。這里的“PermGen space”其實(shí)指的就是方法區(qū)。不過方法區(qū)和“PermGen space”又有著本質(zhì)的區(qū)別够坐。前者是JVM的規(guī)范寸宵,而后者則是JVM規(guī)范的一種實(shí)現(xiàn),并且只有HotSpot才有“PermGen space”元咙,而對(duì)于其他類型的虛擬機(jī)梯影,如JRockit(Oracle)、J9(IBM)并沒有“PermGen space”庶香。由于方法區(qū)主要存儲(chǔ)類的相關(guān)信息甲棍,所以對(duì)于動(dòng)態(tài)生成類的情況比較容易出現(xiàn)永久代的內(nèi)存溢出。并且JDK 1.8中參數(shù)PermSize和MaxPermSize已經(jīng)失效赶掖。

元空間

其實(shí)感猛,移除永久代的工作從JDK 1.7就開始了七扰。JDK 1.7中,存儲(chǔ)在永久代的部分?jǐn)?shù)據(jù)就已經(jīng)轉(zhuǎn)移到Java Heap或者Native Heap唱遭。但永久代仍存在于JDK 1.7中戳寸,并沒有完全移除,譬如符號(hào)引用(Symbols)轉(zhuǎn)移到了native heap拷泽;字面量(interned strings)轉(zhuǎn)移到了Java heap疫鹊;類的靜態(tài)變量(class statics)轉(zhuǎn)移到了Java heap。

JDK1.8對(duì)JVM架構(gòu)的改造將類元數(shù)據(jù)放到本地內(nèi)存中司致,另外拆吆,將常量池和靜態(tài)變量放到Java堆里。HotSpot VM將會(huì)為類的元數(shù)據(jù)明確分配和釋放本地內(nèi)存脂矫。在這種架構(gòu)下枣耀,類元信息就突破了原來-XX:MaxPermSize的限制,現(xiàn)在可以使用更多的本地內(nèi)存庭再。這樣就從一定程度上解決了原來在運(yùn)行時(shí)生成大量類造成經(jīng)常Full GC問題捞奕,如運(yùn)行時(shí)使用反射、代理等拄轻。所以升級(jí)以后Java堆空間可能會(huì)增加颅围。

元空間的本質(zhì)和永久代類似,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)恨搓。不過元空間與永久代之間的最大區(qū)別在于:元空間并不在虛擬機(jī)中院促,而是使用本地內(nèi)存。因此斧抱,默認(rèn)情況下常拓,元空間的大小僅受本地內(nèi)存限制,但可以通過以下參數(shù)指定元空間的大谢云帧:

-XX:MetaspaceSize弄抬,初始空間大小,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載宪郊,同時(shí)GC會(huì)對(duì)改值進(jìn)行調(diào)整:如果釋放了大量的空間眉睹,就適當(dāng)降低該值;如果釋放了很少的空間废膘,那么在不超過MaxMetaspaceSize時(shí),適當(dāng)提高該值慕蔚。

-XX:MaxMetaspaceSize丐黄,最大空間,默認(rèn)是沒有限制的孔飒。

除了上面的兩個(gè)指定大小的選項(xiàng)外灌闺,還有兩個(gè)與GC相關(guān)的屬性:

-XX:MinMetaspaceFreeRatio艰争,在GC之后,最小的Metaspace剩余空間容量的百分比桂对,減少為分配空間所導(dǎo)致的垃圾收集甩卓。

-XX:MaxMetaspaceFreeRatio,在GC之后蕉斜,最大的Metaspace剩余空間容量的百分比逾柿,減少為釋放空間所導(dǎo)致的垃圾收集。

所以對(duì)于方法區(qū)宅此,Java8之后的變化:

移除了永久代(PermGen)机错,替換為元空間(Metaspace);
永久代中的 class metadata 轉(zhuǎn)移到了 native memory(本地內(nèi)存父腕,而不是虛擬機(jī))弱匪;
永久代中的 interned Strings 和 class static variables 轉(zhuǎn)移到了 Java heap;
永久代參數(shù) (PermSize MaxPermSize) -> 元空間參數(shù)(MetaspaceSize MaxMetaspaceSize)

1.2 虛擬機(jī)棧(線程棧)與 堆(Heap)

為更好的理解Java線程棧和堆璧亮,我們簡(jiǎn)單的認(rèn)為Java內(nèi)存模型把Java虛擬機(jī)內(nèi)部劃分為線程棧和堆萧诫。這張圖演示了Java內(nèi)存模型的邏輯視圖。

每一個(gè)運(yùn)行在Java虛擬機(jī)里的線程都擁有自己的線程棧枝嘶。這個(gè)線程棧包含了這個(gè)線程調(diào)用的方法當(dāng)前執(zhí)行點(diǎn)相關(guān)的信息帘饶。一個(gè)線程僅能訪問自己的線程棧。一個(gè)線程創(chuàng)建的本地變量對(duì)其它線程不可見躬络,僅自己可見尖奔。即使兩個(gè)線程執(zhí)行同樣的代碼,這兩個(gè)線程任然在在自己的線程棧中的代碼來創(chuàng)建本地變量穷当。因此提茁,每個(gè)線程擁有每個(gè)本地變量的獨(dú)有版本。

所有原始類型的本地變量都存放在線程棧上馁菜,因此對(duì)其它線程不可見茴扁。一個(gè)線程可能向另一個(gè)線程傳遞一個(gè)原始類型變量的拷貝,但是它不能共享這個(gè)原始類型變量自身汪疮。

堆上包含在Java程序中創(chuàng)建的所有對(duì)象峭火,無論是哪一個(gè)對(duì)象創(chuàng)建的。這包括原始類型的對(duì)象版本智嚷。如果一個(gè)對(duì)象被創(chuàng)建然后賦值給一個(gè)局部變量卖丸,或者用來作為另一個(gè)對(duì)象的成員變量,這個(gè)對(duì)象任然是存放在堆上盏道。

下面這張圖演示了調(diào)用棧和本地變量存放在線程棧上稍浆,對(duì)象存放在堆上。

一個(gè)本地變量可能是原始類型,在這種情況下衅枫,它總是“呆在”線程棧上嫁艇。

一個(gè)本地變量也可能是指向一個(gè)對(duì)象的一個(gè)引用。在這種情況下弦撩,引用(這個(gè)本地變量)存放在線程棧上步咪,但是對(duì)象本身存放在堆上。

一個(gè)對(duì)象可能包含方法益楼,這些方法可能包含本地變量猾漫。這些本地變量任然存放在線程棧上,即使這些方法所屬的對(duì)象存放在堆上偏形。

一個(gè)對(duì)象的成員變量可能隨著這個(gè)對(duì)象自身存放在堆上静袖。不管這個(gè)成員變量是原始類型還是引用類型。

靜態(tài)成員變量跟隨著類定義一起也存放在堆上俊扭。

存放在堆上的對(duì)象可以被所有持有對(duì)這個(gè)對(duì)象引用的線程訪問队橙。當(dāng)一個(gè)線程可以訪問一個(gè)對(duì)象時(shí),它也可以訪問這個(gè)對(duì)象的成員變量萨惑。如果兩個(gè)線程同時(shí)調(diào)用同一個(gè)對(duì)象上的同一個(gè)方法捐康,它們將會(huì)都訪問這個(gè)對(duì)象的成員變量,但是每一個(gè)線程都擁有這個(gè)本地變量的私有拷貝庸蔼。

下圖演示了上面提到的點(diǎn):

兩個(gè)線程擁有一些列的本地變量解总。其中一個(gè)本地變量(Local Variable 2)執(zhí)行堆上的一個(gè)共享對(duì)象(Object 3)。這兩個(gè)線程分別擁有同一個(gè)對(duì)象的不同引用姐仅。這些引用都是本地變量花枫,因此存放在各自線程的線程棧上。這兩個(gè)不同的引用指向堆上同一個(gè)對(duì)象掏膏。

注意劳翰,這個(gè)共享對(duì)象(Object 3)持有Object2和Object4一個(gè)引用作為其成員變量(如圖中Object3指向Object2和Object4的箭頭)。通過在Object3中這些成員變量引用馒疹,這兩個(gè)線程就可以訪問Object2和Object4佳簸。

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

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器颖变。分支生均、循環(huán)、跳轉(zhuǎn)腥刹、異常處理马胧、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。

由于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ǔ)抱冷,我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存。

如果線程正在執(zhí)行的是一個(gè)Java 方法梢褐,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址旺遮;如果正在執(zhí)行的是Natvie 方法,這個(gè)計(jì)數(shù)器值則為空(Undefined)盈咳。

此內(nèi)存區(qū)域是唯一一個(gè)在Java 虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域耿眉。

1.4 本地方法棧

本地方法棧(Native MethodStacks)與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java 方法(也就是字節(jié)碼)服務(wù)鱼响,而本地方法棧則是為虛擬機(jī)使用到的Native 方法服務(wù)鸣剪。虛擬機(jī)規(guī)范中對(duì)本地方法棧中的方法使用的語(yǔ)言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定丈积,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它筐骇。甚至有的虛擬機(jī)(譬如Sun HotSpot 虛擬機(jī))直接就把本地方法棧和虛擬機(jī)棧合二為一。

與虛擬機(jī)棧一樣江滨,本地方法棧區(qū)域也會(huì)拋出StackOverflowError和OutOfMemoryError異常铛纬。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市唬滑,隨后出現(xiàn)的幾起案子告唆,更是在濱河造成了極大的恐慌,老刑警劉巖晶密,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擒悬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惹挟,警方通過查閱死者的電腦和手機(jī)茄螃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來连锯,“玉大人归苍,你說我怎么就攤上這事≡瞬溃” “怎么了拼弃?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)摇展。 經(jīng)常有香客問我吻氧,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任盯孙,我火速辦了婚禮鲁森,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘振惰。我一直安慰自己歌溉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布骑晶。 她就那樣靜靜地躺著痛垛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桶蛔。 梳的紋絲不亂的頭發(fā)上匙头,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音仔雷,去河邊找鬼蹂析。 笑死,一個(gè)胖子當(dāng)著我的面吹牛朽寞,可吹牛的內(nèi)容都是我干的识窿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脑融,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼喻频!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肘迎,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤甥温,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后妓布,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姻蚓,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年匣沼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狰挡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡释涛,死狀恐怖加叁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唇撬,我是刑警寧澤它匕,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窖认,受9級(jí)特大地震影響豫柬,放射性物質(zhì)發(fā)生泄漏告希。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一烧给、第九天 我趴在偏房一處隱蔽的房頂上張望燕偶。 院中可真熱鬧,春花似錦创夜、人聲如沸杭跪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至系奉,卻和暖如春檬贰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缺亮。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工翁涤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萌踱。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓葵礼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親并鸵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸳粉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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