從JVM的內(nèi)存分區(qū)到對象如何使用它們

全文概括

? 虛擬機(jī)可以看作一臺(tái)抽象的計(jì)算機(jī)企蹭,有自己的指令集和運(yùn)行時(shí)內(nèi)存分區(qū)雳攘。堆和方法區(qū)是線程共享的又兵,隨JVM創(chuàng)建消亡任柜。棧和PC計(jì)數(shù)器是線程私有的。

? 堆是最大的一塊沛厨,為對象分配內(nèi)存就是從堆中分配宙地。分配時(shí)需要考慮并發(fā)操作。

? 方法區(qū)存放各個(gè)類的信息逆皮,包括運(yùn)行時(shí)常量池宅粥、字節(jié)碼、和初始化方法电谣。

? 一個(gè)線程一個(gè)棧秽梅,一個(gè)方法一個(gè)棧幀抹蚀。棧幀中有局部變量表、操作數(shù)棧和常量池指針企垦。

? 將類信息加載到方法區(qū)中况鸣,對象實(shí)例在堆中分配內(nèi)存,使棧的本地變量中對象的引用指向堆中的實(shí)例竹观,實(shí)例再指向方法區(qū)的類型镐捧。然后執(zhí)行<init>初始化這塊實(shí)例內(nèi)存。

正文?

全部手敲臭增,如果需要轉(zhuǎn)載請注明出處懂酱。

主要知識(shí)來自《Java虛擬機(jī)規(guī)范》(Java SE8) 周志明翻譯的那本。還有《深入理解Java虛擬機(jī):JVM高級特性與最佳實(shí)踐》

此外還參考了下面這些鏈接文章:http://www.reibang.com/p/eaef248b5a2c誊抛;https://blog.csdn.net/uotail/article/details/83373794

? Java虛擬機(jī)可以看作一臺(tái)抽象的計(jì)算機(jī)列牺,有自己的指令集運(yùn)行時(shí)內(nèi)存區(qū)域

? 這些分區(qū)是邏輯上的,是抽象的拗窃。和計(jì)算機(jī)的實(shí)際內(nèi)存是不同的瞎领。比如Java棧實(shí)際上是被計(jì)算機(jī)分配在計(jì)算機(jī)的堆上的。

? 這些分區(qū)有些是所有線程共享的随夸,隨著虛擬機(jī)的創(chuàng)建和銷毀九默。有些是線程私有的,每個(gè)線程都有一份宾毒,隨著線程創(chuàng)建和銷毀

一驼修、 線程共享的區(qū)域

1.1 堆 Java Heap

? 被所有線程共享的一塊內(nèi)存區(qū)域,主要用于存放對象實(shí)例诈铛。

? 是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊兒乙各,物理上可以不連續(xù)。所有的對象實(shí)例和數(shù)據(jù)都要在堆上進(jìn)行分配幢竹。這些對象被GC管理著耳峦。

1.1.1 為對象分配內(nèi)存的方法*

? 為對象分配內(nèi)存就是把一塊大小確定的內(nèi)存從堆內(nèi)存中劃分出來。

? Java虛擬機(jī)采用哪種方式為新生對象分配內(nèi)存焕毫,取決于所使用的垃圾收集器蹲坷。

? 為了分帶垃圾收集,堆可以細(xì)分為新生代(細(xì)分為Eden:from:to)和老年代(Old Generation)咬荷。

? 當(dāng)垃圾收集器具有整理過程時(shí)冠句,虛擬機(jī)將采用指針碰撞的方式轻掩;當(dāng)垃圾收集器的回收過程沒有整理過程時(shí)幸乒,則采用空閑列表方式。

指針碰撞法

? 已分配的內(nèi)存和空閑內(nèi)存分別在不同的一側(cè)唇牧,通過一個(gè)指針作為分界點(diǎn)罕扎,需要分配內(nèi)存時(shí)聚唐,僅僅需要把指針往空閑的一端移動(dòng)與對象大小相等的距離。

空閑列表法

? 已分配的內(nèi)存和空閑內(nèi)存相互交錯(cuò)腔召,JVM通過維護(hù)一個(gè)列表杆查,記錄可用的內(nèi)存塊信息

1.1.2 多線程如何并發(fā)分配內(nèi)存?

對象創(chuàng)建是一個(gè)非常頻繁的行為臀蛛,進(jìn)行堆內(nèi)存分配時(shí)還需要考慮多線程并發(fā)問題亲桦,可能出現(xiàn)正在給對象A分配內(nèi)存,指針或記錄還未更新浊仆,對象B又同時(shí)分配到原來的內(nèi)存客峭,解決這個(gè)問題有兩種方案:

CAS(Compare And Swap)*

? 比較并替換,實(shí)現(xiàn)并發(fā)算法時(shí)常用到的一種技術(shù)抡柿。在Java中舔琅,主要在Atomic包,調(diào)用Unsafe類相關(guān)方法體現(xiàn)洲劣。

? 涉及三個(gè)值:內(nèi)存中真正存的值(可能被其他線程改變)备蚓、邏輯上的原值、(當(dāng)前線程)要寫入的新值囱稽。通過循環(huán)檢查內(nèi)存中的值是不是原來的值郊尝,以此來判斷是不是正有其他線程在改變它。判斷成功立即寫入新值战惊,這一步是原子的虚循。

? 存在ABA問題,即內(nèi)存中雖然還是邏輯上原來的值样傍,但是已經(jīng)被變成B過了横缔。這個(gè)問題JDK1.5已經(jīng)通過AtomicStampedReference類解決,將對象追加一個(gè)版本號(hào)stamp衫哥,這樣每次改變過都是一個(gè)“全新的值”

? 具體可以看我的這篇文章http://www.reibang.com/p/c8e9bce8b3c6

本地線程分配緩沖(Thread Local Allocation Buffer, TLAB)

? 把內(nèi)存分配的行為按照線程進(jìn)行劃分茎刚,在不同的空間中進(jìn)行,每個(gè)線程在Java堆中預(yù)先分配一個(gè)內(nèi)存塊撤逢。哪個(gè)線程要分配內(nèi)存膛锭,就在它的TLAB上分配,如果用完了蚊荣,再追加時(shí)再去考慮同步初狰。JVM參數(shù)中-XX:+/-UseTLAB可以設(shè)定是否使用此策略。

1.1.3 堆溢出

? 堆是用來存儲(chǔ)對象實(shí)例的互例。如果對象達(dá)到一定的數(shù)量奢入,并且這些對象和根對象之間有可達(dá)路徑,那么就不能被垃圾收集器回收媳叨,超過堆的最大容量腥光,就會(huì)出現(xiàn)溢出关顷,拋OutOfMemoryError

? 具體原因可能有兩種。首先可能是出現(xiàn)了死循環(huán)武福。其次可能是啟動(dòng)JVM時(shí)分配的堆太小议双,如-Xms100m -Xmx200m設(shè)置堆最小100最大200m。

1.2 方法區(qū)

? 方法區(qū)用于每個(gè)類的結(jié)構(gòu)信息和一些特殊方法捉片。類的結(jié)構(gòu)信息不僅有字段和方法的數(shù)據(jù)平痰、構(gòu)造函數(shù)和普通方法的字節(jié)碼,還有運(yùn)行時(shí)常量池伍纫。而特殊方法主要是指實(shí)例初始化<init>方法觉增、類或接口初始化<clinit>方法。

1.2.1 類的結(jié)構(gòu)信息

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

? 它包括了若干種不同的常量翻斟,從編譯期可知的數(shù)值字面量到必須在運(yùn)行期解析后才能獲得的方法或者字段引用逾礁。

? 一些虛擬機(jī),只想對象實(shí)例的引用是一個(gè)指向句柄的指針访惜。這個(gè)句柄由包含了兩個(gè)指針:一個(gè)指向該對象各個(gè)方法及對應(yīng)的Class對象的表格嘹履。另一個(gè)指向在堆中分配的數(shù)據(jù)。

? 但HotSpot VM中债热,指向?qū)ο蟮囊貌⒉煌ㄟ^句柄砾嫉,而是直接指向堆中對象的實(shí)例數(shù)據(jù)。這個(gè)詳見第三部分窒篱。

1.2.2 初始化方法*

<init>實(shí)例初始化方法

? 只能在實(shí)例初始化期間通過JVM的invokespecial指令調(diào)用焕刮。且只能在尚為初始化的實(shí)例上調(diào)用一次。

<clinit>類或接口初始化方法

? 由JVM自身隱式調(diào)用墙杯,沒有任何JVM字節(jié)碼指令可以調(diào)用這個(gè)方法配并。只會(huì)在類的初始化階段中由虛擬機(jī)自身調(diào)用

1.2.3 方法區(qū)溢出

如果運(yùn)行時(shí)方法區(qū)產(chǎn)生了大量的類,超出了方法區(qū)的最大容量高镐,將拋出OutOfMemoryError異常溉旋。

二、線程私有的分區(qū)

2.1 Java棧

? Java棧是線程私有的嫉髓,是Java方法的執(zhí)行模型观腊。每個(gè)線程對應(yīng)一個(gè)棧,每個(gè)線程在執(zhí)行一個(gè)方法時(shí)會(huì)創(chuàng)建一個(gè)對應(yīng)的棧幀(Stack Frame)算行,棧幀負(fù)責(zé)存儲(chǔ)局部變量變量表梧油、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回地址等信息州邢。每個(gè)方法的調(diào)用過程儡陨,相當(dāng)于棧幀在Java棧的入棧和出棧過程

2.1.1 棧幀

? 隨方法調(diào)用創(chuàng)建和銷毀,同時(shí)可能存在多個(gè),但只有棧頂?shù)漠?dāng)前棧幀是活動(dòng)的迄委。

12421988-22c1ea0b98238988.png

如上圖褐筛,一個(gè)線程中的方法調(diào)用鏈可能會(huì)很長类少,很多方法都同時(shí)處于執(zhí)行狀態(tài)叙身。對于執(zhí)行引擎來講,活動(dòng)線程中硫狞,只有虛擬機(jī)棧頂?shù)臈攀怯行У男沤危Q為當(dāng)前棧幀(Current Stack Frame),這個(gè)棧幀所關(guān)聯(lián)的方法稱為當(dāng)前方法(Current Method)残吩。

局部變量表 Local Variable Table

? 局部變量存放了編譯器可知的各種類型财忽,即JVM可操作的類型。

? 通過方法的code屬性保存及提供給棧幀使用泣侮。局部變量表所需的內(nèi)存空間在編譯期間完成分配即彪。

? 以變量槽(Slot)為最小存儲(chǔ)單位,可以放32bit活尊。對于long和double隶校,以高位對齊方式分配兩個(gè)連續(xù)Slot。

JVM可操作的數(shù)據(jù)類型

原始類型 primitive

  • 數(shù)值類型 numeric type

    整型(byte/short/int/long/char)和浮點(diǎn)型(float/double)

  • boolean類型

    false是0蛹锰,true是1深胳。沒有專門的字節(jié)碼指令,只能轉(zhuǎn)換成byte或int再操作

  • returnAddress類型

    指向一條JVM操作碼opcode铜犬,原始類型中唯一一個(gè)不能直接和Java的數(shù)據(jù)類型相對應(yīng)的舞终。無法在程序運(yùn)行期間修改。

引用類型 reference

  • 類class 類型
  • 數(shù)組array 類型
  • 接口interface類型
  • null
字節(jié)碼指令

? 字節(jié)開頭字母大多指明了為哪種數(shù)據(jù)類型服務(wù):
a-reference癣猾,d-double敛劝,f-float,c-char纷宇,b-byte攘蔽,s-short,l-lomg呐粘,i-int

? 使用索引定位訪問满俗,第0個(gè)位置一定是用來存儲(chǔ)this,即當(dāng)前對象的引用作岖。隨便把一個(gè)a.java文件javac編譯一下唆垃,再用javap -verbose a就可以看到任意一個(gè)非static的方法,前兩句指令都是aload_0(將this拿到)和dup(裝入操作數(shù)棧)痘儡。就是方便線程訪問當(dāng)前對象的類信息和類域

操作數(shù)棧 operand stack

? 用來暫存指令取出來的操作數(shù)辕万,也用來準(zhǔn)備調(diào)用方法的參數(shù)及接收方法返回的結(jié)果。

常量池指針 constant_pool point

? 動(dòng)態(tài)鏈接,在類加載的過程中渐尿,將代碼中的符號(hào)引用轉(zhuǎn)換成了直接引用醉途。

2.1.2 棧溢出

? 如果線程請求的棧深度,大于虛擬機(jī)所允許的深度砖茸,將拋出StackoverflowError異常隘擎。

? 但棧的深度也不是越深越好,不然一個(gè)線程就要耗費(fèi)很多內(nèi)存凉夯,如果擴(kuò)展時(shí)無法申請到足夠的內(nèi)存货葬,就會(huì)拋出OutOfMemoryError異常。?

2.2 本地方法棧

? 執(zhí)行native方法劲够,也會(huì)拋出StackoverflowError異常和OutOfMemoryError異常震桶,有的虛擬機(jī)(如HotSpot VM)將本地方法棧與上面的虛擬機(jī)棧合二為一。

2.3 PC指令計(jì)數(shù)器 program counter

? 記錄著JVM正在執(zhí)行的opcode的地址征绎。分支蹲姐、循環(huán)、跳轉(zhuǎn)人柿、異常處理和線程恢復(fù)等操作都依賴這個(gè)計(jì)數(shù)器完成柴墩。如果當(dāng)前方法是native,pc寄存器是undefined顷扩。

三拐邪、對象怎么使用JVM內(nèi)存分區(qū)

3.1 在內(nèi)存中的布局

? 以HotSpot為例對象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對象頭(Header)、實(shí)例數(shù)據(jù)(Instance)隘截、對象填充(Padding)扎阶。

285763-20170807173010534-177864240.png

3.1.1 對象頭

markword

? 用來存儲(chǔ)對象自身的運(yùn)行時(shí)數(shù)據(jù),例如hashcode婶芭、GC年代东臀、鎖狀態(tài)、線程持有的鎖犀农、偏向線程的ID惰赋、偏向時(shí)間戳等。

Class對象指針

? 也叫類型指針呵哨,指向這個(gè)對象的類的元數(shù)據(jù)赁濒,JVM樂意根據(jù)它知道這個(gè)對象是哪個(gè)類的實(shí)例。

元數(shù)據(jù)(meta data)

? 結(jié)構(gòu)化的數(shù)據(jù)孟害。將任何信息拆解成元數(shù)據(jù)項(xiàng)目(名字/鍵)和元數(shù)據(jù)內(nèi)容(值)拒炎,并且可以有層次。比如XML中挨务,每個(gè)標(biāo)簽都是一個(gè)元數(shù)據(jù)击你。每個(gè)標(biāo)簽可以有多個(gè)子標(biāo)簽玉组,每個(gè)標(biāo)簽都有自己的屬性,他們表明了這個(gè)元數(shù)據(jù)要表達(dá)的意圖丁侄。

3.1.2 實(shí)例數(shù)據(jù)

? 即程序中的字段惯雳,包括父類繼承來的。這部分的存儲(chǔ)順序和虛擬機(jī)還有源碼中的位置都有關(guān)系鸿摇,HotSpot默認(rèn)是相同寬度的字段在一起石景。long/double> int > short/char > byte/boolean > oop(Ordinary Object Pointers)。但子類中較窄的可能會(huì)插入父類的空隙里户辱。

3.1.3 對象填充

? HotSpot VM 要求對象的起始地址是8字節(jié)的整數(shù)倍鸵钝。

3.2 對象怎么創(chuàng)建

? 在JVM遇到new時(shí)(這里是普通的對象非數(shù)組或Class)糙臼,檢查這個(gè)類符號(hào)引用在不在方法區(qū)的常量池中庐镐,進(jìn)而檢查是否被加載過。如果沒有变逃,首先要執(zhí)行類加載必逆。

? 如果已加載,那么要給新對象分配內(nèi)存揽乱,即在Java堆中劃出一塊內(nèi)存來(大小在類加載完成確定)名眉。

? 將分配到的內(nèi)存(除對象頭外)都初始化為0。如果使用TLAB凰棉,則這一步需提到TLAB前损拢。

? 根據(jù)對象頭,對這個(gè)對象進(jìn)行必要設(shè)置撒犀。

? 至此福压,對于JVM,新對象已經(jīng)產(chǎn)生或舞。但對于Java程序來說荆姆,對象創(chuàng)建是這之后執(zhí)行<init>的過程。

? 字節(jié)碼中如果跟隨了invokespecial指令映凳,那么執(zhí)行new指令后接著執(zhí)行方法區(qū)的<init>方法胆筒。

3.3 對象的引用

? Java程序通過棧上的引用類型reference來操作堆中的具體對象。目前主流的訪問方式有兩種诈豌,句柄和直接指針仆救。HotSpot VM 使用的是直接指針。

句柄訪問模式

20170213153426104.png

? 好處是矫渔,reference值不變彤蔽,在對象被移動(dòng)(GC時(shí)需要常常移動(dòng)),只改變了句柄中的實(shí)例數(shù)據(jù)指針蚌斩。

直接指針訪問

20170213153623949.png

? 好處是節(jié)省了一次指針定位的時(shí)間開銷铆惑。對象的訪問是一件非常頻繁的事范嘱。

謝謝觀看,如果有用麻煩點(diǎn)個(gè)喜歡员魏,對我是莫大的鼓勵(lì)丑蛤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市撕阎,隨后出現(xiàn)的幾起案子受裹,更是在濱河造成了極大的恐慌,老刑警劉巖虏束,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棉饶,死亡現(xiàn)場離奇詭異,居然都是意外死亡镇匀,警方通過查閱死者的電腦和手機(jī)照藻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汗侵,“玉大人幸缕,你說我怎么就攤上這事∥希” “怎么了发乔?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雪猪。 經(jīng)常有香客問我栏尚,道長,這世上最難降的妖魔是什么只恨? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任译仗,我火速辦了婚禮,結(jié)果婚禮上坤次,老公的妹妹穿的比我還像新娘古劲。我一直安慰自己,他們只是感情好缰猴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布产艾。 她就那樣靜靜地躺著,像睡著了一般滑绒。 火紅的嫁衣襯著肌膚如雪闷堡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天疑故,我揣著相機(jī)與錄音杠览,去河邊找鬼。 笑死纵势,一個(gè)胖子當(dāng)著我的面吹牛踱阿,可吹牛的內(nèi)容都是我干的管钳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼软舌,長吁一口氣:“原來是場噩夢啊……” “哼才漆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起佛点,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤醇滥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后超营,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸳玩,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年演闭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了不跟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡船响,死狀恐怖躬拢,靈堂內(nèi)的尸體忽然破棺而出躲履,到底是詐尸還是另有隱情见间,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布工猜,位于F島的核電站米诉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏篷帅。R本人自食惡果不足惜史侣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魏身。 院中可真熱鬧惊橱,春花似錦、人聲如沸箭昵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽家制。三九已至正林,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颤殴,已是汗流浹背觅廓。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涵但,地道東北人杈绸。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓帖蔓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瞳脓。 傳聞我的和親對象是個(gè)殘疾皇子讨阻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

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

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司篡殷,掛了不少钝吮,但最終還是拿到小米、百度板辽、阿里奇瘦、京東、新浪劲弦、CVTE耳标、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,239評論 11 349
  • Java8張圖 11、字符串不變性 12邑跪、equals()方法次坡、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,701評論 0 11
  • 在一個(gè)方法內(nèi)部定義的變量都存儲(chǔ)在棧中画畅,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后砸琅,其對應(yīng)的棧就會(huì)被回收,此時(shí)轴踱,在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,416評論 1 14
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,097評論 1 32
  • 一:java概述: 1症脂,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境淫僻,java的開發(fā)...
    慕容小偉閱讀 1,788評論 0 10