【Java】JVM入門解析(二)

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

(圖片來源於網絡,侵刪)


一、運行時數據區概述

架構圖👇
在這裏插入圖片描述
Java虛擬機定義了若干種程序運行期間會使用到的運行時數據區,其中有一些會隨着虛擬機啓動而創建,隨着虛擬機退出而銷燬。另外一些則是與線程一一對應的,這些線程對應的數據區域會隨着線程開始和結束而創建和銷燬

線程獨享:

  • 程序計數器(PC寄存器)、棧、本地棧

線程共享:

  • 堆、方法區(永久代或元空間)

二、程序計數器(PC寄存器)

簡介
  • 1)它是一塊很小的內存空間,幾乎可以忽略不計。同時也是運行速度最快的存儲區域
  • 2)在JVM規範中,每個線程都有它自己的程序計數器,是線程私有的,生命週期與線程的生命週期保持一致
  • 3)任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的Java方法的JVM指令地址,或者,如果是在執行native方法,則是未指定值(undefined)
作用

PC寄存器是用來存儲指向下一條指令的地址,也就是即將要指向的指令代碼。由執行引擎讀取下一條指令

架構圖如下👇
在這裏插入圖片描述

思考:PC寄存器爲什麼會被設定未線程私有?

我們都知道所謂的多線程,在一個特定的時間段內只會執行其中某一個線程的方法,CPU會不停的切換任務,這樣必然導致經常中斷或恢復,那麼如何保證分毫無差呢?
爲了能夠準確地記錄各個線程正在執行地當前字節碼地指令地址,就給每一個線程都分配了一個PC寄存器,這樣每個線程之間便可以進行獨立計算,從而不會出現相互干擾了情況

CPU時間片

CPU時間片即CPU分配給各個程序的時間,每個線程被分配一個時間段,稱作它的時間片
在宏觀上:我們可以同時打開多個應用程序,每個程序並行不悖,同時運行
但是在微觀上:由於只有一個CPU,一次只能處理程序要求的一部分,如果公平處理,所以引入時間片,每個程序輪流執行

三、虛擬機棧

內存中的棧與堆

棧解決程序的運行問題,即程序如何執行,或者說如何處理數據
堆解決數據存儲問題,即數據怎麼放、放在哪兒

虛擬機棧

在這裏插入圖片描述
虛擬機棧簡介及特點:

  • 1)每個線程在創建的時候都會創建一個虛擬機棧,其內部保存一個個的棧幀,對應着一次次的Java方法調用
  • 2)棧式一種快速有效的分配存儲方式,訪問速度僅次於程序計數器
  • 3)JVM直接對Java棧的操作只有兩個
    • 每個方法執行,伴隨着入棧/壓棧
    • 方法執行結束後的出棧
  • 4)在一條活動線程中,一個時間點上,只會有一個活動的棧幀。即只有當前正在執行的方法的棧幀是有效的,這個棧幀被稱爲當前棧幀,與當前棧幀相對應的方法就是當前方法,定義這個方法的類就是當前類
  • 5)執行引擎運行的所有字節碼指令只對當前棧幀進行操作
  • 6)如果在該方法中調用了其他方法,對應的新的棧幀就會被創建出來,放在棧的頂端,成爲新的當前棧幀
  • 7)不同線程中所包含的棧幀是不允許存在相互引用的,即不可能在一個棧幀之中引用另外一個線程的棧幀
  • 8)如果當前方法調用了其他方法,方法返回之際,當前棧幀會傳回此方法的執行結果給前一個棧幀,接着,虛擬機會丟棄當前棧幀,使得前一個棧幀重新成爲當前棧幀

在這裏插入圖片描述

生命週期:
生命週期和線程一致

作用:
主管Java程序的運行,它保存方法的局部變量(8種基本數據類型、對象的引用地址)、部分結果,並參與方法的調用和返回

棧不存在垃圾回收問題

棧中可能出現的異常:
Java虛擬機規範允許Java棧的大小是動態的或者固定不變的

  • 1)如果採用固定大小的Java虛擬機棧,那每一個線程的Java虛擬機棧容量可以在線程創建的時候獨立選定。如果線程請求分配的棧容量超過了Java虛擬機棧允許的最大容量,Java虛擬機會拋出一個 StackOverflowError 異常
  • 2)如果Java虛擬機棧可以動態擴展,並且在嘗試擴展的時候無法申請到足夠的內存,或者在創建新的線程時沒有足夠的內存去創建對應的虛擬機棧時,Java虛擬機會拋出一個 OutOfMemoryError異常

設置棧內存大小:
我們可以使用參數 -Xss 選項來設置線程的最大棧空間,棧的大小直接決定了函數調用的最大可達深度

棧中存儲什麼?

  • 1)每個線程都有自己的棧,棧中的數據都是以棧幀的格式存在
  • 2)在這個線程上正在執行的每個方法都各自對應一個棧幀
  • 3)棧幀是一個內存區塊,是一個數據集,維繫着方法執行過程中的各種數據信息

棧幀的內部結構

每個棧幀中存儲着:

  • 1)局部變量表
  • 2)操作數棧
  • 3)動態鏈接
  • 4)方法返回地址
  • 5)附加信息

在這裏插入圖片描述

局部變量表

1)概述
  • 局部變量表也被稱之爲局部變量數組或本地變量表
  • 局部變量表被定義爲一個數字數組,主要用於存儲方法參數和定義在方法體內的局部變量,這些數據類型包括各類基本數據類型、對象引用(reference),以及returnAddress類型
  • 由於局部變量表是建立在線程的棧上,是線程的私有數據,因此不存在數據安全問題
  • 局部變量表所需的容量大小(slot大小)是在編譯器確定下來的,並保存在方法的Code屬性的maximum local variables數據項中。在方法運行期間是不會改變局部變量表的大小的
  • 方法嵌套調用的次數由棧的大小決定。一般來說,棧越大,方法嵌套調用的次數越多
  • 局部變量表中的變量只在當前方法調用中有效。當方法調用結束後,隨着方法棧幀的銷燬,局部變量表也會隨之銷燬
2)關於Slot
  • 局部變量表最基本的存儲單元是Slot(變量槽)
  • 局部變量表中存放編譯期可知的各種基本數據類型(8種),引用類型地址,returnAddress類型的變量
  • 在局部變量表裏,32位以內的類型只佔用一個slot(包括returnAddress類型),64位的類型(long、double)佔用兩個slot
  • JVM會被局部變量表中的每一個Slot都分配一個訪問索引,通過這個索引即可成功訪問到局部變量表中指定的局部變量值
  • 當一個實例方法被調用時,它的方法參數和方法體內部定義的局部變量將會按照順序被賦值到局部變量表中的每一個slot上
  • 如果需要訪問局部變量表中一個64位的局部變量值時,只需要使用前一個索引即可(Long、Double)
  • 如果當前棧幀是由構造方法或者實例方法創建的,那麼該對象引用this將會放在index位0的slot上,其餘的參數按照參數表順序繼續排列

在這裏插入圖片描述

3)Slot的重複利用

棧幀中的局部變量表中的slot是可以重用的,如果一個局部變量過了其作用域,那麼在其作用域之後聲明的新的局部變量就很可能會重複利用過期局部變量的槽位,從而達到節省資源的目的

示例👇:
在這裏插入圖片描述

4)總結

在棧幀中,與性能調優關係最爲密切的就是局部變量表,在方法執行時,虛擬機使用局部變量表完成方法的傳遞
局部變量表中的變量也是重要的垃圾回收根節點,只要被局部變量表中直接或間接引用的對象都不會回收

操作數棧

概述
  • 每一個獨立的棧幀中除了包含局部變量表以外,還包含一個後進先出的操作數棧
  • 操作數棧,在方法執行過程中,根據字節碼指令,往棧中寫入數據或提取數據,即入棧和出棧
  • 操作數棧,主要用於保存計算過程的中間結果,同時作爲計算過程中變量臨時的存儲空間
  • 操作數棧是JVM執行引擎的一個工作區,當一個方法剛開始執行時,一個新的棧幀也會隨之被創建出來,這個方法的操作數棧時空的
  • 每一個操作數棧都會擁有一個明確的棧深度用於存儲數值,其所需的最大深度在編譯期就定義好了,保存在方法的Code屬性中,爲max_stack的值
  • 棧中的任何一個元素都可以是任意的Java數據類型
    • 32位的類型佔用一個棧單位深度
    • 64位的類型佔用兩個棧單位深度

操作數棧雖然底層是由數組實現的,但並非採用訪問索引的方式進行數據訪問,而是通過標準的入棧和出棧操作來完成一次數據訪問

動態鏈接(指向運行時常量池的方法引用)

概述
  • 每一個棧幀內部都包含一個指向方法區中的運行時常量池該棧幀所屬方法的引用。包含這個引用的目的就是爲了支持當前方法的代碼能夠實現動態鏈接
  • 在Java源文件被編譯到字節碼文件中時,所有的變量和方法引用都作爲符號引用保存在class文件的常量池裏

在這裏插入圖片描述

爲什麼需要常量池?

常量池的作用,就是爲了提供一些符號和常量,便於指令的識別

方法的調用

在JVM中,將符號引用轉換爲調用方法的直接引用與方法的綁定機制相關

  • 靜態鏈接:
    • 當一個字節碼文件被裝載進JVM內部時,如果被調用的目標方法在編譯器可知,且運行期保持不變。這種情況下將調用方法的符號引用轉換爲直接引用的過程稱之爲靜態鏈接
  • 動態鏈接:
    • 如果被調用的方法在編譯器無法被確定下來,也就是說,只能夠在程序運行期將調用方法的符號引用轉換爲直接引用,由於這種引用轉換過程具備動態性,因此也被稱之爲動態鏈接

虛方法和非虛方法

  • 非虛方法:如果方法在編譯器就確定了具體的調用版本,這個版本在運行時是不可變的,這樣的方法稱爲非虛方法
  • 靜態方法、私有方法、final方法、實例構造器、父類方法都是非虛方法,其他方法成爲虛方法

方法返回地址

概述
  • 方法返回地址即存放調用該方法的PC寄存器的值
  • 一個方法的結束,有兩種方式:
    • 正常執行完成
    • 出現未處理的異常,非正常退出

無論通過哪種方式退出,在方法退出後都返回到該方法被調用的位置。方法正常退出時,調用者的PC寄存器的值作爲返回地址,即調用該方法的指令的下一條指令的地址。而通過異常退出的,返回地址是要通過異常表來確定,棧幀中一般不會保存這部分信息

三、本地方法棧

什麼是本地方法?

簡答來講,一個Native Method就是一個Java調用非Java代碼地接口
一個Native Method是這樣一個Java方法:該方法地實現由非Java語言實現
在定義一個Native Method時,並不提供實現體,因爲其實現體是由非Java語言在外面實現地
本地接口地作用是融合不同地編程語言爲Java所用


都看到這裏了,點贊評論一下吧!!!

在這裏插入圖片描述

點擊查看👇

【Java】JVM入門解析(三)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章