版權(quán)聲明:本文為斑馬君學(xué)習(xí)總結(jié)文章,轉(zhuǎn)載請(qǐng)注明出處!
一囊卜、jdk、jre眠菇、jvm之間的關(guān)系
從廣義上講边败,運(yùn)行于java虛擬機(jī)上的語(yǔ)音及其相關(guān)的程序都屬于java技術(shù)體系中的一員。Sun官方所定義的java技術(shù)體系包括以下幾個(gè)組成部分:
- 1 java程序設(shè)計(jì)語(yǔ)言
- 2 各種硬件平臺(tái)上的java虛擬機(jī)
- 3 Class文件格式
- 4 Java API 類(lèi)庫(kù)
-
5 第三方Java類(lèi)庫(kù)
把Java程序設(shè)計(jì)語(yǔ)言捎废、java虛擬笑窜、javaAPI類(lèi)庫(kù)這三部分統(tǒng)稱(chēng)為JDK。把JavaAPI類(lèi)庫(kù)中的JavaSE API子集和Java虛擬機(jī)這兩部分統(tǒng)稱(chēng)為JRE.JRE是java程序運(yùn)行的標(biāo)準(zhǔn)環(huán)境登疗。Jvm: Java Virtual Machine java虛擬機(jī)排截。三者之間關(guān)系圖。
二辐益、Java虛擬機(jī)內(nèi)存管理
虛擬機(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)毀。
方法區(qū):存儲(chǔ)運(yùn)行時(shí)常量池续捂,已被虛擬機(jī)加載的類(lèi)信息垦垂,常量,靜態(tài)變量牙瓢,即時(shí)編譯器編譯后的代碼等數(shù)據(jù)劫拗。
Java堆:存儲(chǔ)對(duì)象實(shí)例。
2.2線(xiàn)程獨(dú)占區(qū)
本地方法棧:為jvm所調(diào)用到的Native腳本地方法服務(wù)矾克。
虛擬機(jī)棧:存放方法運(yùn)行時(shí)所需的數(shù)據(jù)页慷,成為棧幀。
程序計(jì)數(shù)器:記錄當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼的行號(hào)。
三酒繁、程序計(jì)數(shù)器
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間滓彰,它可以看作是當(dāng)前線(xiàn)程所執(zhí)行的字節(jié)碼的行號(hào)指示器。程序計(jì)數(shù)器處于線(xiàn)程獨(dú)占區(qū)欲逃。
如果線(xiàn)程執(zhí)行的是Java方法找蜜,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址。如果正在執(zhí)行的是native方法稳析,這個(gè)計(jì)數(shù)器的值為undefined洗做。此區(qū)域是唯一一個(gè)在java虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutOfMemoryError情況的區(qū)域。
四彰居、虛擬機(jī)棧
虛擬機(jī)棧描述的是java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量表诚纸、操作數(shù)棧、動(dòng)態(tài)鏈接陈惰、方法出口等信息畦徘。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過(guò)程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過(guò)程抬闯。
局部變量表:存放編譯期可知的各種基本數(shù)據(jù)類(lèi)型,引用類(lèi)型胀屿,returnAddress類(lèi)型塘揣。
局部變量表在內(nèi)存空間的編譯期完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí)宿崭,這個(gè)方法需要在幀分配多少內(nèi)存是固定的亲铡,在方法運(yùn)行期間是不會(huì)改變局部變量表的大小。
如果線(xiàn)程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度葡兑,將拋出StackOverflowError異常奴愉;如果虛擬機(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ì)拋出OutOfMemoryError異常蜕劝。
五、本地方法棧
與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,它們之間的區(qū)別不過(guò)是虛擬機(jī)棧為虛擬機(jī)執(zhí)行java方法(也就是字節(jié)碼)服務(wù)岖沛,而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)暑始。
六、Java堆
存放對(duì)象事例婴削。垃圾收集器管理的主要區(qū)域廊镜,新生代,老年代唉俗。Eden空間嗤朴。通過(guò)-Xmx -Xms參數(shù)來(lái)控制堆內(nèi)存的大小。如果在堆中沒(méi)有內(nèi)存完成實(shí)例分配虫溜,并且堆也無(wú)法再擴(kuò)展時(shí)雹姊,將會(huì)拋出OutOfMemoryError異常。
七衡楞、方法區(qū)
存儲(chǔ)虛擬機(jī)加載的類(lèi)信息吱雏,常量,靜態(tài)變量瘾境,即時(shí)編譯器編譯后的代碼等數(shù)據(jù)歧杏。方法區(qū)并不等價(jià)于永久代。這區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和堆類(lèi)型的卸載迷守。
八犬绒、運(yùn)行時(shí)常量池
方法區(qū)的一部分。Class文件中除了有類(lèi)的版本盒犹、字段懂更、方法、接口等描述信息外急膀。還有一項(xiàng)信息是常量池沮协。用于存放編譯期生成的各種字面量和符號(hào)引用,者部分內(nèi)存將在類(lèi)加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放卓嫂。比如String str =”abc”慷暂。
九、直接內(nèi)存:
并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分晨雳。也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域行瑞。新加入了NIO類(lèi),引入了一個(gè)基于通道和緩沖區(qū)的I/O方式餐禁,它可以使用Native函數(shù)庫(kù)直接分配堆外內(nèi)存血久,然后通過(guò)一個(gè)存儲(chǔ)在java堆中的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作。
對(duì)象的創(chuàng)建
對(duì)象的創(chuàng)建主要分四部分:1.給對(duì)象分配內(nèi)存 2.線(xiàn)程安全性問(wèn)題 3.初始化對(duì)象 4.執(zhí)行構(gòu)造方法
線(xiàn)程安全問(wèn)題:對(duì)象創(chuàng)建在虛擬機(jī)中是非常頻繁的行為熙卡,即使是僅僅修改一個(gè)指針?biāo)赶虻奈恢谜人ⅲ诓l(fā)情況下也并不是線(xiàn)程安全的,可能出現(xiàn)正在給對(duì)象A分配內(nèi)存驳癌,指針還沒(méi)來(lái)得及修改滑燃,對(duì)象B又同時(shí)使用了原來(lái)的指針來(lái)分配內(nèi)存的情況。
方案一:一種是對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理颓鲜,實(shí)際上虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性表窘。
方案二:把內(nèi)存分配的動(dòng)作按照線(xiàn)程劃分在不同的空間之中進(jìn)行,即每個(gè)線(xiàn)程在Java堆中預(yù)先分配一小塊內(nèi)存甜滨,稱(chēng)為本地線(xiàn)程分配緩沖(Thread Local Allocation Buffer,TLAB)乐严。哪個(gè)線(xiàn)程要分配內(nèi)存,就在哪個(gè)線(xiàn)程的TLAB上分配衣摩,只有TLAB用完并分配新的TLAB時(shí)昂验,才需要同步鎖定。虛擬機(jī)是否使用TLAB艾扮,可以通過(guò)-XX:+/-UseTLAB參數(shù)來(lái)設(shè)定既琴。
執(zhí)行構(gòu)造方法:虛擬機(jī)要對(duì)對(duì)象進(jìn)行必要的設(shè)置甫恩,例如這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例厂汗,如何才能找到類(lèi)的元數(shù)據(jù)信息鼠冕、對(duì)象的哈希碼爷贫、對(duì)象的GC分代年齡等信息步势。
對(duì)象的內(nèi)存布局
在HotSpot虛擬機(jī)中丹诀,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭(Header)喝滞、實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding)异旧。
1.Header(對(duì)象頭)包含兩部分信息涵卵,第一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)(Mark Word) 哈希碼 GC分代年齡 鎖狀態(tài)標(biāo)志 線(xiàn)程持有的鎖莱褒,偏向線(xiàn)程ID 偏向時(shí)間戳击困。根據(jù)鎖的狀態(tài)不同,把這個(gè)對(duì)象頭區(qū)域記錄不同的信息广凸,
2.類(lèi)型指針:即對(duì)象指向它的類(lèi)元數(shù)據(jù)的指針阅茶,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例。
3.InstanceData:是對(duì)象真正存儲(chǔ)的有效數(shù)據(jù)谅海。也是在程序代碼中所定義的各種類(lèi)型的字段內(nèi)容脸哀。這部分的存儲(chǔ)順序會(huì)受到虛擬機(jī)分配策略參數(shù)和字段在java源碼中定義順序的影響。
4.Padding:對(duì)齊填充并不是必然存在的扭吁,它僅僅起著占位符的作用撞蜂。
對(duì)象的訪(fǎng)問(wèn)和定位
Java程序需要通過(guò)棧上的reference數(shù)據(jù)來(lái)操作堆上的具體對(duì)象。由于reference類(lèi)型在java虛擬機(jī)規(guī)范中只規(guī)定一個(gè)指向?qū)ο蟮囊媒耐啵](méi)有定義這個(gè)引用應(yīng)該通過(guò)何種方式去定位蝌诡、訪(fǎng)問(wèn)堆中的對(duì)象的具體位置,所以對(duì)象訪(fǎng)問(wèn)方式也是取決于虛擬機(jī)實(shí)現(xiàn)而定的枫吧。目前主流的訪(fǎng)問(wèn)方式有句柄和直接指針兩種浦旱。
1.使用句柄:如果使用句柄訪(fǎng)問(wèn)的話(huà),那么java堆中將會(huì)劃分一塊內(nèi)存來(lái)作為句柄池九杂,reference中存儲(chǔ)的就是對(duì)象的句柄地址颁湖,而句柄包含了對(duì)象實(shí)例數(shù)據(jù)與類(lèi)型數(shù)據(jù)各自的具體地址信息。