主要內(nèi)容如下:
一,、JVM啟動(dòng)流程: JVM啟動(dòng)時(shí),,是由java命令/javaw命令來(lái)啟動(dòng)的。 二,、JVM基本結(jié)構(gòu): JVM基本結(jié)構(gòu)圖: 《深入理解Java虛擬機(jī)(第二版)》中的描述是下面這個(gè)樣子的:
Java中的內(nèi)存分配: Java程序在運(yùn)行時(shí),,需要在內(nèi)存中的分配空間。為了提高運(yùn)算效率,,就對(duì)數(shù)據(jù)進(jìn)行了不同空間的劃分,,因?yàn)槊恳黄瑓^(qū)域都有特定的處理數(shù)據(jù)方式和內(nèi)存管理方式。 具體劃分為如下5個(gè)內(nèi)存空間:(非常重要)
1,、程序計(jì)數(shù)器:
2、方法區(qū):
3,、堆內(nèi)存:
現(xiàn)在的GC基本都采用分代收集算法,如果是分代的,,那么堆也是分代的,。如果堆是分代的,那堆空間應(yīng)該是下面這個(gè)樣子: 上圖是堆的基本結(jié)構(gòu),,在之后的文章中再進(jìn)行詳解,。 4、棧內(nèi)存:
解釋?zhuān)?/p> Java虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被調(diào)用的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,,用于存儲(chǔ)局部變量表、操作棧,、動(dòng)態(tài)鏈接,、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過(guò)程就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中從入棧到出棧的過(guò)程,。 在Java虛擬機(jī)規(guī)范中,,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常情況: (1)如果線(xiàn)程請(qǐng)求的棧深度太深,超出了虛擬機(jī)所允許的深度,,就會(huì)出現(xiàn)StackOverFlowError(比如無(wú)限遞歸,。因?yàn)槊恳粚訔颊加靡欢臻g,而 Xss 規(guī)定了棧的最大空間,,超出這個(gè)值就會(huì)報(bào)錯(cuò)) (2)虛擬機(jī)??梢詣?dòng)態(tài)擴(kuò)展,如果擴(kuò)展到無(wú)法申請(qǐng)足夠的內(nèi)存空間,,會(huì)出現(xiàn)OOM
4.1 Java棧之局部變量表:包含參數(shù)和局部變量 局部變量表存放了基本數(shù)據(jù)類(lèi)型,、對(duì)象引用和returnAddress類(lèi)型(指向一條字節(jié)碼指令的地址)。其中64位長(zhǎng)度的long和double類(lèi)型的數(shù)據(jù)會(huì)占用2個(gè)局部變量空間(slot),,其余數(shù)據(jù)類(lèi)型只占用1個(gè),。局部變量表所需的內(nèi)存空間在編譯期間完成分配。 例如,,我寫(xiě)出下面這段代碼: 1 package test03; 2 3 /** 4 * Created by smyhvae on 2015/8/15. 5 */ 6 public class StackDemo { 7 8 //靜態(tài)方法 9 public static int runStatic(int i, long l, float f, Object o, byte b) { 10 return 0; 11 } 12 13 //實(shí)例方法 14 public int runInstance(char c, short s, boolean b) { 15 return 0; 16 } 17 18 }
上方代碼中,,靜態(tài)方法有6個(gè)形參,實(shí)例方法有3個(gè)形參,。其對(duì)應(yīng)的局部變量表如下: 上方表格中,,靜態(tài)方法和實(shí)例方法對(duì)應(yīng)的局部變量表基本類(lèi)似。但有以下區(qū)別:實(shí)例方法的表中,,第一個(gè)位置存放的是當(dāng)前對(duì)象的引用,。
4、2 Java棧之函數(shù)調(diào)用組成棧幀: 方法每次被調(diào)用的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,例如下面這個(gè)方法: public static int runStatic(int i,long l,float f,Object o ,byte b){ return runStatic(i,l,f,o,b); }
當(dāng)它每次被調(diào)用的時(shí)候,,都會(huì)創(chuàng)建一個(gè)幀,,方法調(diào)用結(jié)束后,幀出棧,。如下圖所示:
4.3 Java棧之操作數(shù)棧 Java沒(méi)有寄存器,所有參數(shù)傳遞都是使用操作數(shù)棧 例如下面這段代碼: public static int add(int a,int b){ int c=0; c=a+b; return c; }
壓棧的步驟如下: 0: iconst_0 // 0壓棧 1: istore_2 // 彈出int,,存放于局部變量2 2: iload_0 // 把局部變量0壓棧 3: iload_1 // 局部變量1壓棧 4: iadd //彈出2個(gè)變量,,求和,結(jié)果壓棧 5: istore_2 //彈出結(jié)果,,放于局部變量2 6: iload_2 //局部變量2壓棧 7: ireturn //返回 如果計(jì)算100+98的值,,那么操作數(shù)棧的變化如下圖所示:
4.4 Java棧之棧上分配:
棧,、堆,、方法區(qū)交互:
三、內(nèi)存模型: 每一個(gè)線(xiàn)程有一個(gè)工作內(nèi)存,。工作內(nèi)存和主存獨(dú)立,。工作內(nèi)存存放主存中變量的值的拷貝。 當(dāng)數(shù)據(jù)從主內(nèi)存復(fù)制到工作存儲(chǔ)時(shí),,必須出現(xiàn)兩個(gè)動(dòng)作:第一,,由主內(nèi)存執(zhí)行的讀(read)操作;第二,,由工作內(nèi)存執(zhí)行的相應(yīng)的load操作,;當(dāng)數(shù)據(jù)從工作內(nèi)存拷貝到主內(nèi)存時(shí),也出現(xiàn)兩個(gè)操作:第一個(gè),,由工作內(nèi)存執(zhí)行的存儲(chǔ)(store)操作,;第二,由主內(nèi)存執(zhí)行的相應(yīng)的寫(xiě)(write)操作,。 每一個(gè)操作都是原子的,,即執(zhí)行期間不會(huì)被中斷 對(duì)于普通變量,一個(gè)線(xiàn)程中更新的值,,不能馬上反應(yīng)在其他變量中,。如果需要在其他線(xiàn)程中立即可見(jiàn),需要使用volatile關(guān)鍵字作為標(biāo)識(shí),。 1,、可見(jiàn)性: 一個(gè)線(xiàn)程修改了變量,其他線(xiàn)程可以立即知道 保證可見(jiàn)性的方法:
2,、有序性: 在本線(xiàn)程內(nèi),操作都是有序的 在線(xiàn)程外觀察,,操作都是無(wú)序的,。(指令重排 或 主內(nèi)存同步延時(shí)) 3、指令重排: 指令重排:破壞了線(xiàn)程間的有序性:
指令重排:保證有序性的方法: 指令重排的基本原則:
四、解釋運(yùn)行和編譯運(yùn)行的概念: 解釋運(yùn)行:
編譯運(yùn)行(JIT):
編譯運(yùn)行的性能優(yōu)于解釋運(yùn)行,。
|
|
來(lái)自: primivite_ > 《jvm》