若是咱們對計算機組成有所瞭解,那麼咱們必定會知道在計算機中有一起特殊的區域,稱之爲寄存器,寄存器包括了指令寄存器和程序計數器,這兩樣位於CPU中,做爲程序運行的大腦來控制程序的運行和流轉。java
而在JVM中,做爲一種虛擬機,JVM沒有指令寄存器,它是基於棧 + 程序計數器的體系結構來完成方法的執行,之因此這麼去設計一方面是爲了指令集的緊湊,一方面是有些平臺上的寄存器不多或者根本沒有,並且以處理器架構的角度來講,設計一套通用的寄存器指令是很困難的,並且還有一方面的考量就是有助於運行時某些虛擬機實現的動態編譯器和即時編譯器的代碼優化。多線程
咱們在使用IDE寫代碼的時候,旁邊常常會有行數,方便咱們去閱讀咱們本身的代碼,去定位咱們代碼的位置,而程序計數器是給JVM執行字節碼過程當中看的行號指示器,只不過他其中存的並不是是行號,而是執行中虛擬機字節碼指令的地址,也正是由於計數器中存儲的僅僅是一條執行中的指令地址,使用的內存是及其有限的,因此這個區域是惟一一個沒有OOM的區域。字節碼解釋器經過改變程序計數器中的地址來尋找對應的指令來完成對程序的控制(這裏具體咱們會在分析指令集時去詳細的深刻了解,這裏僅僅點到爲止)架構
這裏須要注意一點,若是是本地Native 方法,該計數器的值是Undefined,其實也很好理解,人家壓根不屬於JVM去管理,你憑什麼去記錄。。這裏咱們使用的能夠說是Native方法提供出的一個接口,具體的實現是經過C來完成的。優化
上節課咱們說到多線程的實現是基於時分複用來實現的,爲了每一個線程的運行的互不干擾和有序性,程序計數器必須保證在切換時可以回到正確的位置,因此它必須也必然是線程獨佔區的一份子。spa
關鍵字:線程
虛擬機棧是Java方法運行時的內存模型,包括了局部變量表,操做數棧,動態鏈接,方法返回地址以及一些附加信息,每一個方法在被執行的時候會在虛擬機棧中創造一個棧幀。設計
棧幀在執行的時候建立壓入棧,在徹底執行完成後就會進行彈棧,下面是一個小🌰去直觀的看一下過程~3d
(小聲BB一句,這裏不會製做GIF圖,湊合着看吧。。)code
下面簡單介紹一下棧幀中的內容,這一部分咱們會在瞭解字節碼執行引擎的時候會進行更爲詳細的講解。cdn
局部變量表
局部變量表(Local Variable Table)是一組變量值存儲空間,用於方法參數和方法內部定義的局部變量,包含了編譯器可知的基本數據類型(byte,short,int,long,double,float,char,boolean),對象引用(reference)以及returnAddress類型。
操做數棧
一樣是一個LIFO(Last In First Out,後入先出)的棧結構,用於計算的臨時存儲區,前面提過JVM是沒有寄存器的,而操做數棧的做用就是讓JVM的指令是從中獲取操做數。
動態鏈接
每一個棧幀都包含一個運行時常量池中該棧幀所屬方法的引用,持有這個引用就是爲了支持方法在調用過程當中的動態鏈接
和虛擬機棧類似,本地方法棧面向的對象不是JVM字節碼,而是本地的Native方法,好比String類中的intern方法,Native方法更像是本地方法的提供的一個接口,而本地方法棧正是管理這些接口的一個區域。
public native String intern();
複製代碼
本地方法棧和虛擬機棧同爲棧結構之中,他們會面對兩種異常,當線程請求的棧深度大於虛擬機所容許的深度時,將拋出StackOverflowError,若是棧能夠動態擴展,若是擴展時沒法申請到足夠的內存,會拋出OOM異常