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

JVM 的棧與寄存器

1. 前言

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

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

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

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

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

3. 棧的基本介紹

基本概念:Java 棧有兩個,分別是虛擬機棧和本地方法棧。這里以虛擬機棧為例,本地方法棧和虛擬機?;鞠嗤?。

棧的特點:對于每個線程,將創(chuàng)建單獨的運行時棧。對于每個方法調(diào)用,將在棧存儲器中產(chǎn)生一個條目,稱為棧幀。所有局部變量將在棧內(nèi)存中創(chuàng)建。棧區(qū)域是線程安全的,因為它不共享資源。

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

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

4. 棧幀

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

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

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

圖片描述

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

5. 棧幀 - 局部變量表

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

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

特點

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

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

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

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

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

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

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

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

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

8. 寄存器簡介

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

寄存器簡介

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

9. 寄存器的特點

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

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

10. 小結(jié)

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