Java虛擬機02-JVM運行時數(shù)據(jù)區(qū)

1 JVM運行時數(shù)據(jù)區(qū)

JVM運行時數(shù)據(jù)區(qū)(JVM Runtime Area)其實就是指JVM在運行期間,其對計算機內(nèi)存空間的劃分和分配搀军。

image

2 程序計數(shù)器

是一塊較小的內(nèi)存空間膨俐,存儲下一條需要執(zhí)行的Java字節(jié)碼指令的地址(字節(jié)碼指令存儲在方法區(qū)中)勇皇。JVM的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻焚刺,一個處理器(對于多核處理器來說是一個內(nèi)核)都只能執(zhí)行一條線程中的指令敛摘。因此每條線程需要一個獨立的程序計數(shù)器,在能保證線程切換后能恢復(fù)到正確的執(zhí)行位置乳愉。

字節(jié)碼指令

在啟動的一個Java進程時兄淫,JVM會將class文件數(shù)據(jù)加載到方法區(qū)中。方法區(qū)中存儲Class文件數(shù)據(jù)其中包含的類信息和Java字節(jié)碼指令蔓姚。

源碼

public class ClassStructureMethod {
    public  void greeting() throws Exception {
       try {
           int a=1;
           int b=1;
           int c=a+b;
           System.out.println(c);
       }catch (Exception e){
           System.out.println("catch");
       }finally {
           System.out.println("finally");
       }
    }
}

greeting方法Code屬性中字節(jié)碼指令

...省略
public void greeting() throws java.lang.Exception;
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_1
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: iload_3
        12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: ldc           #4                  // String finally
        20: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        23: goto          59
        26: astore_1
        27: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: ldc           #7                  // String catch
        32: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        35: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        38: ldc           #4                  // String finally
        40: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        43: goto          59
        46: astore        4
        48: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        51: ldc           #4                  // String finally
        53: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        56: aload         4
        58: athrow
        59: return
        

3 方法區(qū)

  • 方法區(qū)主要用來存儲加載的Class文件信息拖叙,其中包括了常量池類信息,字段信息,方法信息(字節(jié)碼指令),"指向類加載器的引言"赂乐,"指向Class的引言"薯鳍,"方法表"。
image
  • 此區(qū)域的內(nèi)存回收目標(biāo)主要是針對無用類做卸載挨措,一般來說挖滤,回收效果難以令人滿意,尤其是類型的卸載浅役,條件相對苛刻斩松,但是這部分區(qū)域回收是有必要的。

  • 當(dāng)方法無法滿足內(nèi)存需求時觉既,將會拋出OutOfMemoryError異常

4 Java虛擬機棧

  • Java虛擬機棧是一個后入先出棧惧盹。每一個線程創(chuàng)建時,JVM會為這個線程創(chuàng)建一個私有虛擬機棧瞪讼。

  • 當(dāng)線程調(diào)用某個對象的方法時钧椰,JVM會相應(yīng)地創(chuàng)建一個棧幀壓入虛擬機棧中,返回時從虛擬機棧中取出符欠。線程方法的調(diào)用返回對應(yīng)著一個棧幀在虛擬機棧中的入棧和出棧的過程嫡霞。

  • 當(dāng)前虛擬機正在執(zhí)行的方法的棧幀被稱為"當(dāng)前活動的棧幀",永遠位于虛擬機棧頂部。

image
4.1 虛擬機棧中的方法調(diào)用

案例

public class Demo3 {

    public void test1() {
        System.out.println("我是test1的方法");
        test2();
    }
    
    public void test2() {
        System.out.println("我是test2的方法");
        test3();
    }
    
    public void test3() {
        System.out.println("我是test3的方法");
    }
    
    public static void main(String[] args) {
        Demo3 demo3=new Demo3();
        demo3.test1();
    }
}

虛擬機棧中的流程

虛擬機棧執(zhí)行過程.jpg
4.2 棧幀結(jié)構(gòu)
image
4.2.1 局部變量表
  • 局部變量表(Local Variable Table)是一組變量值存儲空間(類似于數(shù)組)希柿,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量诊沪。局部變量表的容量以變量槽(Slot)為最小單位,如果局部變量表類似于數(shù)組曾撤,那么變量槽(Slot)就相當(dāng)于數(shù)組中一個數(shù)據(jù)塊的大小端姚。

  • 局部變量表中變量最大數(shù)量在將源碼文件編譯成Class文件時就以確定,存儲在方法的Code屬性locals中挤悉。

  • 需要注意的是如果是實例方法(非static的方法)渐裸,那么局部變量表中第0位索引的Slot默認是用于傳遞方法所屬對象實例的引用。

局部變量表存儲數(shù)據(jù)類型

  • 八大數(shù)據(jù)類型(boolean、byte橄仆、char、short衅斩、int盆顾、float、long畏梆、double)

  • 對象引用

  • returnAddress類型(指向了一條字節(jié)碼指令的地址)

存儲數(shù)據(jù)方式

  • 32位虛擬機中一個Slot可以存放一個32位以內(nèi)的數(shù)據(jù)類型(boolean您宪、byte、char奠涌、short宪巨、int、float溜畅、reference和returnAddress八種)捏卓。對于long、double這種64位數(shù)據(jù)類型 值需要用兩個(Slot)來存儲.

  • 在局部變量表中慈格,boolean怠晴、byte、char浴捆、short引用類型這五種類型蒜田,在虛擬機棧中空間和int是一樣的都占用8個字節(jié),而long选泻、double占用16個字節(jié)冲粤。

4.2.2 操作數(shù)棧

操作數(shù)棧是用來存儲Java字節(jié)碼指令計算過程中的參數(shù)和結(jié)果的數(shù)據(jù)結(jié)構(gòu)。Java虛擬機的解釋執(zhí)行引擎被稱為"基于棧的執(zhí)行引擎"页眯,其中所指的棧就是-操作數(shù)棧梯捕。

棧的深度已經(jīng)在將源碼文件編譯成Class文件時就以確定,存儲在方法的Code屬性stack中窝撵。

操作數(shù)棧的存儲

操作數(shù)棧也是被組織成一個以字長為單位的數(shù)組科阎,不同的是,它不是通過索引來訪問忿族,而是通過標(biāo)準(zhǔn)的棧操作—壓棧和出椔啾浚—來訪問的。比如道批,如果某個指令把一個值壓入到操作數(shù)棧中错英,稍后另一個指令就可以彈出這個值來使用。

虛擬機在操作數(shù)棧中存儲數(shù)據(jù)的方式和在局部變量區(qū)中是一樣的隆豹。對于非long椭岩、double都使用8個字節(jié)存儲占用1個變量槽,對于long、double都使用16個字節(jié)存儲占用2個變量槽判哥,這也應(yīng)證了Java虛擬機對數(shù)據(jù)的操作就是在操作數(shù)棧和局部變量表中相互傳遞献雅。

操作數(shù)棧的案例

begin 
iload_0 // 將第一個int型本地變量推送至棧頂 
iload_1 // 將第二個int型本地變量推送至棧頂 
iadd // 將棧頂兩int型數(shù)值相加并將結(jié)果壓入棧頂 
istore_2 // 將棧頂int型數(shù)值存入第三個本地變量 
end  

前兩個指令iload_0和iload_1將存儲在局部變量中索引為0和1的整數(shù)壓入操作數(shù)棧中,其后iadd指令從操作數(shù)棧中彈出那兩個整數(shù)相加塌计,再將結(jié)果壓入操作數(shù)棧挺身。第四條指令istore_2則從操作數(shù)棧中彈出結(jié)果,并把它存儲到局部變量區(qū)索引為2的位置锌仅。下圖詳細表述了這個過程中局部變量和操作數(shù)棧的狀態(tài)變化章钾,圖中沒有使用的局部變量區(qū)和操作數(shù)棧區(qū)域以空白表示。

image
4.2.3 方法返回地址

方法返回在虛擬機中流程

  • 恢復(fù)上層方法的局部變量表和操作數(shù)棧

  • 把返回值壓入調(diào)用者調(diào)用者棧幀的操作數(shù)棧

  • 調(diào)整 PC 計數(shù)器的值以指向方法調(diào)用指令后面的一條指令

方法的正常返回

當(dāng)執(zhí)行遇到返回指令return热芹,會將返回值傳遞給上層的方法調(diào)用者贱傀,這種退出的方式稱為正常完成出口(Normal Method Invocation Completion),一般來說伊脓,調(diào)用者的PC計數(shù)器可以作為返回地址府寒。

方法的異常返回

當(dāng)執(zhí)行遇到異常,并且當(dāng)前方法體內(nèi)沒有得到處理报腔,就會導(dǎo)致方法退出椰棘,此時是沒有返回值的,稱為異常完成出口(Abrupt Method Invocation Completion)榄笙,返回地址要通過異常處理器表來確定邪狞。

4.2.4 動態(tài)鏈接

在了解動態(tài)鏈接之前我們需要了解如下幾個概念,“符號引用”茅撞,“直接引用”帆卓,“靜態(tài)鏈接”

符號引用

在 class 文件被加載至 Java 虛擬機之前,這個類無法知道其他類及其方法米丘、字段所對應(yīng)的具體地址剑令,甚至不知道自己方法、字段的地址拄查。因此吁津,每當(dāng)需要引用這些成員時,Java 編譯器會生成一個符號引用堕扶。符號引用是方法區(qū)中常量池中一類常量碍脏。符號引用用來描述類中,類稍算,接口典尾,字段,方法的描述信息糊探。

對于一個方法調(diào)用钾埂,編譯器會生成一個包含目標(biāo)方法所在類的名字河闰、目標(biāo)方法的名字、接收參數(shù)類型以及返回值類型的符號引用褥紫,來指代所要調(diào)用的方法姜性。
解析階段的目的,正是將這些符號引用解析成為實際引用髓考。如果符號引用指向一個未被加載的類部念,或者未被加載類的字段或方法,那么解析將觸發(fā)這個類的加載(但未必觸發(fā)這個類的鏈接以及初始化绳军。)

#define CONSTANT_Class                      1 //對一個類或接口的符號引用
#define CONSTANT_Fieldref                   2 //對一個字段的符號引用
#define CONSTANT_Methodref                  3 //對一個類中方法的符號引用
#define CONSTANT_InterfaceMethodref         4 //對一個接口中方法的符號引用
  • 對于一個類或接口的描述信息包括類或接口的全限定名稱
#8 = Class              #39            // jvm/ClassStructureMethod
  • 對于一個字段描述信息包括字段所在的類以及描述符印机,描述符包括字段的名稱和類型
#2 = Fieldref           #3.#19         // jvm/TestClass.m:I
  • 對于一個方法描述信息包括方法所在的類以及描述符矢腻,描述符包括方法的名稱门驾,參數(shù)和返回類型
#1 = Methodref          #9.#30         // java/lang/Object."<init>":()V

因此符號引用以一組符號來描述所引用的目標(biāo),符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān)多柑,引用的目標(biāo)并不一定已經(jīng)加載到了內(nèi)存中奶是。

直接引用

直接引用可以是直接指向目標(biāo)在內(nèi)存中的指針,其值中存儲目標(biāo)在內(nèi)存中的地址竣灌。這里所謂的目標(biāo)就是指符號引用表示的類聂沙,接口,字段初嘹,方法及汉。也就是說我們程序執(zhí)行中需要的類在沒有加載到內(nèi)存中時可以使用符號引用來表示。

當(dāng)我們將一個類加載Class文件加載到Java虛擬機的內(nèi)存中屯烦。在解析時會將符號引用轉(zhuǎn)換為直接引用坷随。

靜態(tài)鏈接

靜態(tài)鏈接就是在類加載階段將符號引用轉(zhuǎn)換為直接引用的過程。

在Java中靜態(tài)鏈接就是類加載機制中的一個過程—解析,將class文件中的一部分符號引用直接解析為直接引用的過程驻龟。同時我們也稱它為“解析調(diào)用”

由于Java中多態(tài)的特性温眉,并不所有的符號引用都能在類加載階段轉(zhuǎn)換為直接引用。因此靜態(tài)鏈接需要如下條件翁狐。

  • 方法在程序真正運行之前就有一個可確定的調(diào)用版本类溢,并且這個方法的調(diào)用版本在運行期是不可改變的÷独粒可以概括為:編譯期可知闯冷、運行期不可變。

  • 其中invokestatic和invokespecial指令調(diào)用的方法懈词,都可以在解析階段中確定唯一的調(diào)用版本窃躲,符合這個條件的有靜態(tài)方法、私有方法钦睡、實例構(gòu)造器蒂窒、父類方法

動態(tài)鏈接

動態(tài)鏈接就是在運行期間將符號引用轉(zhuǎn)換為直接引用的過程躁倒。

Java中天生可以動態(tài)擴展的語言特性就是依賴動態(tài)加載和動態(tài)鏈接這個特點實現(xiàn)的。
動態(tài)擴展就是在運行期可以動態(tài)修改字節(jié)碼洒琢,也就是反射機制與cglib

5 本地方法棧

本地方法棧(Native Method Stack)與虛擬機棧所發(fā)揮的作用類似秧秉,它們之間的區(qū)別是:虛擬機棧為虛擬機執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機使用到的Native方法服務(wù)衰抑。

6 堆

對于大多數(shù)應(yīng)用程序而言象迎,Java堆(Heap)是Java虛擬機所管理的內(nèi)存中最大的一塊,它是被所有線程共享的一塊內(nèi)存區(qū)域呛踊,在虛擬機啟動時創(chuàng)建砾淌。此內(nèi)存區(qū)域唯一的目的是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存谭网。

Java虛擬機規(guī)范中描述道:所有的對象實例以及數(shù)組都要在堆上分配般码,但是隨著JIT編譯器的發(fā)展和逃逸分析技術(shù)逐漸成熟奥秆,棧上分配、標(biāo)量替換優(yōu)化技術(shù)將會導(dǎo)致一些微妙的變化發(fā)生,所有的對象都在堆上分配的定論也并不“絕對”了捌袜。

Java堆與垃圾回收器

Java堆是垃圾回收器管理的主要區(qū)域苗缩,因此被稱為“GC堆”(Garbage Collected Heap)扶认。

從內(nèi)存回收角度看
由于目前收集器基本采用分代收集算法唇跨,所以Java堆可細分為:新生代和老年代。

從內(nèi)存分配角度來看
由于堆是線程共享的必然會存在并發(fā)的問題层坠,JVM為每一個線程固定了一個存儲區(qū)域線程私有的分配緩沖區(qū)(TLAB:Thread Local Allocation Buffer)殖妇。

7 直接內(nèi)存

直接內(nèi)存(Direct Memory)并不是虛擬機運行時數(shù)據(jù)的一部分,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域破花。但這部分內(nèi)存也被頻繁運用谦趣,而卻可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。

直接內(nèi)存一般適用適用NIO 適用堆外內(nèi)存的情況

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旧乞,一起剝皮案震驚了整個濱河市蔚润,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尺栖,老刑警劉巖嫡纠,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異延赌,居然都是意外死亡除盏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門挫以,熙熙樓的掌柜王于貴愁眉苦臉地迎上來者蠕,“玉大人,你說我怎么就攤上這事掐松□饴拢” “怎么了粪小?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抡句。 經(jīng)常有香客問我探膊,道長,這世上最難降的妖魔是什么待榔? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任逞壁,我火速辦了婚禮,結(jié)果婚禮上锐锣,老公的妹妹穿的比我還像新娘腌闯。我一直安慰自己,他們只是感情好雕憔,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布姿骏。 她就那樣靜靜地躺著,像睡著了一般橘茉。 火紅的嫁衣襯著肌膚如雪工腋。 梳的紋絲不亂的頭發(fā)上姨丈,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天畅卓,我揣著相機與錄音,去河邊找鬼蟋恬。 笑死翁潘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的歼争。 我是一名探鬼主播拜马,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沐绒!你這毒婦竟也來了俩莽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤乔遮,失蹤者是張志新(化名)和其女友劉穎扮超,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹋肮,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡出刷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坯辩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馁龟。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖漆魔,靈堂內(nèi)的尸體忽然破棺而出坷檩,到底是詐尸還是另有隱情却音,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布矢炼,位于F島的核電站僧家,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏裸删。R本人自食惡果不足惜八拱,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涯塔。 院中可真熱鬧肌稻,春花似錦、人聲如沸匕荸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榛搔。三九已至诺凡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間践惑,已是汗流浹背腹泌。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尔觉,地道東北人凉袱。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像侦铜,于是被迫代替她去往敵國和親专甩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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