第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

JVM 的棧與寄存器

1. 前言

從本節(jié)開始,我們對(duì)運(yùn)行時(shí)數(shù)據(jù)區(qū)進(jìn)行講解,運(yùn)行時(shí)數(shù)據(jù)區(qū)又可以細(xì)分為五個(gè)模塊:棧,堆,寄存器,方法區(qū)和本地方法棧,本節(jié)我們主要針對(duì)講解棧(包括 Java 棧與本地方法棧)與寄存器。本節(jié)主要知識(shí)點(diǎn)如下:

  • 了解棧的基本概念及特點(diǎn),為本節(jié)的基礎(chǔ)知識(shí);
  • 理解并掌握棧幀的概念以及棧幀的數(shù)據(jù)結(jié)構(gòu),并對(duì)棧幀結(jié)構(gòu)中的局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接以及返回地址做詳細(xì)的講解,為本節(jié)核心內(nèi)容,需要重點(diǎn)學(xué)習(xí);
  • 理解并掌握寄存器的概念及作用,為本節(jié)重點(diǎn)內(nèi)容。

2. 運(yùn)行時(shí)數(shù)據(jù)區(qū)知識(shí)回顧

之前我們?cè)谥v解 JVM 整體架構(gòu)的過程中,對(duì)運(yùn)行時(shí)數(shù)據(jù)區(qū)進(jìn)行了總體的概括,運(yùn)行時(shí)數(shù)據(jù)區(qū)又可以細(xì)分為五個(gè)模塊:棧,堆,寄存器,方法區(qū)和本地方法棧,如下圖所示。

本節(jié)我們主要針對(duì)講解棧(Java 棧與和地方法棧)與寄存器(程序計(jì)數(shù)器),其他 2 個(gè)模塊,方法區(qū)和堆會(huì)在后續(xù)的課程中進(jìn)行講解。

3. 棧的基本介紹

基本概念:Java 棧有兩個(gè),分別是虛擬機(jī)棧和本地方法棧。這里以虛擬機(jī)棧為例,本地方法棧和虛擬機(jī)棧基本相同。

棧的特點(diǎn):對(duì)于每個(gè)線程,將創(chuàng)建單獨(dú)的運(yùn)行時(shí)棧。對(duì)于每個(gè)方法調(diào)用,將在棧存儲(chǔ)器中產(chǎn)生一個(gè)條目,稱為棧幀。所有局部變量將在棧內(nèi)存中創(chuàng)建。棧區(qū)域是線程安全的,因?yàn)樗还蚕碣Y源。

  • Java 虛擬機(jī)棧是線程私有的,它的生命周期與線程相同(隨線程而生,隨線程而滅);
  • 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出 StackOverflowError 異常;如果虛擬機(jī)??梢詣?dòng)態(tài)擴(kuò)展,如果擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,就會(huì)拋出 OutOfMemoryError 異常;
  • Java 虛擬機(jī)棧描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀。對(duì)于我們來說,主要關(guān)注的棧內(nèi)存,就是虛擬機(jī)棧中局部變量表部分。

Tips:從棧的特點(diǎn)的最后一點(diǎn)可以看到,開發(fā)者主要關(guān)注的是棧內(nèi)存,而棧內(nèi)存的消耗是因?yàn)槊總€(gè)方法執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀,而占用空間最大的部分就是棧幀的局部變量表部分。后續(xù)我們會(huì)展開講解。

4. 棧幀

定義:棧幀(Stack Frame)是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)。它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的 java 虛擬機(jī)棧的棧元素。棧幀存儲(chǔ)了方法的局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回地址等信息。

棧幀初始化大小:在編譯程序代碼的時(shí)候,棧幀中需要多大的局部變量表內(nèi)存,多深的操作數(shù)棧都已經(jīng)完全確定了。 因此一個(gè)棧幀需要分配多少內(nèi)存,不會(huì)受到程序運(yùn)行期變量數(shù)據(jù)的影響,而僅僅取決于具體的虛擬機(jī)實(shí)現(xiàn)。

棧幀結(jié)構(gòu):如下圖所示,在一個(gè)線程中,只有位于棧頂?shù)臈攀怯行У?,稱為當(dāng)前棧幀,與這個(gè)棧幀相關(guān)聯(lián)的方法稱為當(dāng)前方法。每一個(gè)方法從調(diào)用開始至執(zhí)行完成的過程,都對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)里面從入棧到出棧的過程。

圖片描述

從上圖中我們能夠看到,棧幀的組成結(jié)構(gòu),下文我們將對(duì)局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈表以及返回地址進(jìn)行講解。

5. 棧幀 - 局部變量表

在棧幀中,局部變量表占用了大部分的空間,那么接下來我們看下局部變量表的基本概念與特點(diǎn)。

基本概念:每個(gè)棧幀中都包含一組稱為局部變量表的變量列表,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。

特點(diǎn)

  • 局部變量表的容量以變量槽(Variable Slot)為最小單位;
  • 在方法執(zhí)行過程中,Java 虛擬機(jī)是使用局部變量表完成參數(shù)值到參數(shù)變量列表的傳遞過程;
  • 局部變量表中的 Slot 是可重用的,方法體中定義的變量,其作用域并不一定會(huì)覆蓋整個(gè)方法體,如果當(dāng)前字節(jié)碼程序計(jì)數(shù)器的值已經(jīng)超過了某個(gè)變量的作用域,那么這個(gè)變量相應(yīng)的 Slot 就可以交給其他變量去使用,節(jié)省棧空間。

6. 棧幀 - 操作數(shù)棧

操作數(shù)棧也是棧幀中非常重要的結(jié)構(gòu),操作數(shù)棧不需要占用很大的空間,那么我們一起來看下操作數(shù)棧的作用及特點(diǎn)。

  • 操作數(shù)棧是一個(gè)后入先出(Last In First Out)棧,方法的執(zhí)行操作在操作數(shù)棧中完成,每一個(gè)字節(jié)碼指令往操作數(shù)棧進(jìn)行寫入和提取的過程,就是入棧和出棧的過程;
  • 操作數(shù)棧的每一個(gè)元素可以是任意的 Java 數(shù)據(jù)類型,32 位數(shù)據(jù)類型所占的棧容量為 1,64 位數(shù)據(jù)類型所占的棧容量為 2;
  • 當(dāng)一個(gè)方法剛剛執(zhí)行的時(shí)候,這個(gè)方法的操作數(shù)棧是空的,在方法執(zhí)行的過程中,通過一些字節(jié)碼指令從局部變量表或者對(duì)象實(shí)例字段中復(fù)制常量或者變量值到操作數(shù)棧中。

7. 棧幀 - 動(dòng)態(tài)鏈接與返回地址

動(dòng)態(tài)鏈接的基本概念及作用如下

  • 每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池(JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)域)中該棧幀所屬方法屬性的引用,持有這個(gè)引用是為了支持方法調(diào)用過程中的動(dòng)態(tài)鏈接。
  • 在 Class 文件格式的常量池(存儲(chǔ)字面量和符號(hào)引用)中存有大量的符號(hào)引用(1. 類的全限定名,2. 字段名和屬性,3. 方法名和屬性),字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用為參數(shù)。
    這些符號(hào)引用一部分會(huì)在類加載過程的解析階段的時(shí)候轉(zhuǎn)化為直接引用(指向目標(biāo)的指針、相對(duì)偏移量或者是一個(gè)能夠直接定位到目標(biāo)的句柄),這種轉(zhuǎn)化稱為靜態(tài)解析。另外一部分將在每一次的運(yùn)行期期間轉(zhuǎn)化為直接引用,這部分稱為動(dòng)態(tài)鏈接。

返回地址:返回地址代表的是方法執(zhí)行結(jié)束,方法執(zhí)行結(jié)束有兩種方式,我們來具體看下棧幀中返回地址的作用:

  • 當(dāng)一個(gè)方法開始執(zhí)行后,只有兩種方式可以退出這個(gè)方法。第一種方式是執(zhí)行引擎遇到任意一個(gè)方法返回的字節(jié)碼指令(例如:return),這時(shí)候可能會(huì)有返回值傳遞給上層的方法調(diào)用者(調(diào)用當(dāng)前方法的方法稱為調(diào)用者),是否有返回值和返回值的類型將根據(jù)遇到何種方法返回指令來決定,這種退出方法的方式稱為正常完成出口(Normal Method Invocation Completion)。
  • 另外一種退出方式是,在方法執(zhí)行過程中遇到了異常,并且這個(gè)異常沒有在方法體內(nèi)得到處理,無論是 Java 虛擬機(jī)內(nèi)部產(chǎn)生的異常,還是代碼中使用 throw 字節(jié)碼指令產(chǎn)生的異常,只要在本方法的異常處理器表中沒有搜索到匹配的異常處理器,就會(huì)導(dǎo)致方法退出,這種退出方法的方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個(gè)方法使用異常完成出口的方式退出,是不會(huì)給它的上層調(diào)用者產(chǎn)生任何返回值的。
  • 方法退出的過程實(shí)際上就等同于把當(dāng)前棧幀出棧,因此退出時(shí)可能執(zhí)行的操作有:恢復(fù)上層方法的局部變量表和操作數(shù)棧,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整程序計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令等。

8. 寄存器簡(jiǎn)介

寄存器( PC register )基本概念:每個(gè)線程啟動(dòng)的時(shí)候,都會(huì)創(chuàng)建一個(gè) PC(Program Counter,程序計(jì)數(shù)器)寄存器。PC 寄存器里保存有當(dāng)前正在執(zhí)行的 JVM 指令的地址。

寄存器簡(jiǎn)介

  • 每一個(gè)線程都有它自己的 PC 寄存器,也是該線程啟動(dòng)時(shí)創(chuàng)建的。保存下一條將要執(zhí)行的指令地址的寄存器是:PC 寄存器。PC 寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的地址,這里的地址可以是一個(gè)本地指針,也可以是在方法區(qū)中相對(duì)應(yīng)于該方法起始指令的偏移量;
  • 每個(gè)線程都有一個(gè)寄存器,是線程私有的,其實(shí)就是一個(gè)指針,指向方法區(qū)中的方法字節(jié)碼(用來存儲(chǔ)指向下一條指令的地址,以及即將要執(zhí)行的指令代碼),由執(zhí)行引擎讀取下一條指令,是一個(gè)非常小的內(nèi)存空間,幾乎可以忽略不記;
  • 這塊內(nèi)存區(qū)域很小,它是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,字節(jié)碼解釋器通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。
  • 如果執(zhí)行的是一個(gè) Native 方法,那這個(gè)計(jì)數(shù)器是空的。

9. 寄存器的特點(diǎn)

通過對(duì)寄存器的介紹,我們知道,寄存器器是用來存儲(chǔ)指向下一條指令的地址,以及即將要執(zhí)行的指令代碼。我們來看下寄存器的特點(diǎn):

  • 它是一塊很小的內(nèi)存空間,幾乎可以忽略不記。也是運(yùn)行速度最快的存儲(chǔ)區(qū)域; -
  • 在 JVM 規(guī)范中,每個(gè)線程都有它自己的程序計(jì)數(shù)器,是線程私有的,生命周期與線程的生命周期保持一致;
  • 任何時(shí)間一個(gè)線程都只有一個(gè)方法在執(zhí)行,也就是所謂的當(dāng)前方法。程序計(jì)數(shù)器會(huì)存儲(chǔ)當(dāng)前線程正在執(zhí)行的 java 方法的 JVM 指令地址:或者,如果是在執(zhí)行 native 方法,則是未指定值(undefined);
  • 它是程序控制流的指示器,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成;
  • 字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一個(gè)條需要執(zhí)行的字節(jié)碼指令;
  • 它是唯一一個(gè)在 Java 虛擬機(jī)規(guī)范中沒有規(guī)定任何 OOM 情況的區(qū)域。

10. 小結(jié)

本節(jié)主要講解了運(yùn)行時(shí)數(shù)據(jù)區(qū)的棧與寄存器,其中棧又包括了 Java 棧和本地方法棧,因?yàn)閷?duì)于 Java 棧和本地方法棧,內(nèi)存結(jié)構(gòu)是十分相似的,因此放到一起講解。本節(jié)內(nèi)容中的核心知識(shí)點(diǎn) - 棧幀,有非常多的概念問題,需要學(xué)習(xí)者先做了解,在了解的基礎(chǔ)上,慢慢的消化。