【JVM】內存結構模型

注:本篇主要講解jvm的內存結構、各部分的作用以及各部分之間的潛在聯繫。
在這裏插入圖片描述

一、類加載子系統

java虛擬機的啓動,首先是通過類加載子系統中的引導類加載器創建一個初始類來完成的。這個初始類是虛擬機具體實現需要的class信息,加載後的信息存放在一塊稱之爲方法區的內存空間,至於是否可以運行,需要看執行引擎的工作。

類加載過程

類加載過程中主要包含三個步驟:加載——鏈接——初始化,而鏈接過程中又分爲:驗證——準備——解析
在這裏插入圖片描述

1、加載

  • 通過一個要加載類的權限定名獲取定義此類的二進制字節流。
  • 將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構。
  • 在內存中生成一個代表這個類的java.lang.Class對象,作爲這個類的各種數據在方法區的訪問入口。

常見類加載器

jvm支持兩種類型的類加載器,引導類加載器(Bootstrap ClassLoader )和自定義類加載器(User-Defined ClassLoader),在java虛擬機規範中,將所有派生於抽象類ClassLoader的類加載器都劃分爲自定義類加載器。

引導類加載器-Bootstrap ClassLoader
引導類加載器,又叫啓動類加載器,是虛擬機自帶的一個加載器,用C/C++語言編寫,用來加載jvm核心庫,提供jvm自身需要的類。此類並不繼承java.lang.ClassLoader,沒有父加載器。是擴展類和應用程序類加載器的父類。
出於安全考慮,啓動類加載器只加載包名爲java、javax、sun等開頭的類。

擴展類加載器-Extension ClassLoader
擴展類加載器,有java語言編寫,並且派生於ClassLoader類,父類加載器爲啓動類加載器。
此加載器從java.ext.dirs系統屬性所指定的目錄中加載類庫,或從jdk的安裝目錄jre/lib/ext子目錄下加載類庫、如果用戶創建的jar放在此目錄下,也會自動由擴展類加載器加載。

系統類加載器-AppClassLoader
系統類加載器又叫應用程序類加載器,派生於ClassLoader類,父類加載器爲擴展類加載器。
負責加載環境變量classpath或系統屬性java.class.path指定路徑下的類庫。該類加載器是程序中默認的類加載器,一般來說,java應用的類都是由它來完成的。

雙親委派機制

java虛擬機對class文件採用的是按需加載的方式,當需要使用該類是纔會將它的class文件加載到內存生成class對象。而在加載某個類的class文件時,java虛擬機採用的是雙親委派模式。

原理

  • 如果一個類加載器收到了類加載請求,他並不會自己先去加載,而是把這個請求委託給父類的加載器去執行。
  • 如果父類加載器還存在其父類加載器,則進一步向上委託,依次遞歸,請求最終將到達頂層的啓動類加載器;
  • 如果父類就下載器可以完成類加載任務,就成功返回,無法完成,子加載器纔會嘗試自己去加載
    在這裏插入圖片描述

2、鏈接

在這裏插入圖片描述

3、初始化

在這裏插入圖片描述

二、運行時數據區

在這裏插入圖片描述

1、方法區

方法區又被稱爲永久區,被所有線程共享。用於存放類的信息、常量信息、常量池信息,包括字符串字面量和數字常量等。類信息被加載類子系統加載後,會被存放在方法區進行,而類實例則被存放在堆中。

運行時常量池:是方法區中的一部分,class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用與存放編譯期生成的各種字面量和符號引用,這部分內容將在加載進入方法區的運行時常量池中存放。當方法區中內存滿後,會觸發垃圾回收機制,對方法區中的數據進行垃圾回收。

2、java堆

在java虛擬機啓動時建立,是java程序最主要的內存工作區域,用於存放有new創建的對象和數組,幾乎所有的對象實例都存放到java堆中,被所有線程共享。堆中的內存空間也由java虛擬機自動垃圾回收器來管理。

在堆中產生一個數組或對象後,還會在棧中定義一個特殊的變量,這個變量的取值是堆中這個數組或對象在堆內存中的首地址,棧中的這個變量就是這個數組或對象的引用變量,以後就可以在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象。

根據垃圾回收機制的不同,Java堆有可能擁有不同的結構,最爲常見的就是將整個Java堆分爲新生代和老年代。其中新聲帶存放新生的對象或者年齡不大的對象,老年代則存放老年對象。新生代分爲den區、s0區、s1區,s0和s1也被稱爲from和to區域,他們是兩塊大小相等並且可以互相角色的空間。

絕大多數情況下,對象首先分配在eden區,在新生代回收後,如果對象還存活,則進入s0或s1區,之後每經過一次新生代回收,如果對象存活則它的年齡就加1,對象達到一定的年齡後,則進入老年代。
在這裏插入圖片描述

3、虛擬機棧(運行時棧)

堆是存儲的單位,而棧是運行時的單位。每個線程在創建時都會創建一個虛擬機棧,其內部保存一個個的棧幀,對應着一次次的java方法調用。jvm對java棧的操作有兩個:方法執行進棧、執行結束出棧。所以棧不存在垃圾回收機制。

虛擬機棧主要的作用是:主管java程序的運行,它保存方法的局部變量、部分結果,並參與方法的調用和返回。

原理
不同線程中所包含的棧幀是不允許存在相互引用的,即不可能在一個棧幀中引用另一個線程的棧幀。
如果當前方法調用了其他方法,方法返回之際,當前棧幀會傳回此方法的執行結果給前一個棧幀,接着,虛擬機會丟棄當前棧幀,使得前一個棧幀重新成爲當前棧幀。

棧幀結構

1、局部變量表

  • 定義爲一個數字數組,主要用於存儲方法參數和定義在方法體內的局部變量。
  • 容量大小在編譯期確定,在方法運行期間不會改變。但其大小會影響棧中方法嵌套的次數,容量越大,嵌套次數越少。
  • 局部變量表中的變量只在當前方法調用中有效,隨着方法棧幀的銷燬而銷燬。
  • 最基本存儲單元slot(變量槽)

變量槽-slot

  • 當實例方法被調用時,方法參數和方法體內部定義的局部變量將會按照順序被複制到局部變量表中的每一個slot上。
  • 32位以內的類型佔用一個slot(包括returnAddress類型),64位的類型(long和double)佔用兩個slot。
  • byte、short、char在存儲前被轉換爲int,boolean也被轉換爲int,0表示false,非0表示true。
  • jvm會爲每一個slot都分配一個訪問索引,通過這個索引即可成功訪問到局部變量表中指定的局部變量值。
  • 如果需要訪問一個64位的局部變量值時,只需要使用該變量的第一個slot的索引即可(64位的佔用兩個slot)。
  • 如果當前幀是由構造方法或者實例方法創建的,那麼該對象引用this將會存放在index爲0的slot上,其餘參數按照參數表順序繼續排列。
  • slot是可以重複利用的,如果一個局部變量過了其作用域,那麼在其作用域之後申明的新的局部變量就很有可能會複用過期的局部變量槽位,達到節省資源的目的。

2、操作數棧

又稱表達式棧——後進先出。在方法執行過程中,根據字節碼指令,往棧中寫入數據或提取數據(入棧、出棧)。主要用於保存計算過程的中間結果,同時作爲計算過程中變量臨時的存儲空間。但只能通過標準的入棧出棧操作來完成一次數據訪問。

操作數棧可以是jvm執行引擎的一個工作區,當一個方法剛開始執行的時候,一個新的棧幀隨之被創建,此時這個方法的操作數棧是空的。

操作數棧擁有一定棧深度用於存儲數值,其所需的最大深度在編譯期就定義好了,保存在方法的code屬性中,爲max_stack的值。32bit的類型佔用一個棧單位深度,64bit的類型佔用兩個棧單位深度。如果被調用的方法帶有返回值的話,其返回值將會被壓入當前棧幀的操作數棧中,並更新pc寄存器中下一條需要執行的字節碼指令。

3、方法返回地址

方法返回地址存放調用該方法的pc寄存器的值,當方法退出後都會返回到該方法被調用的位置(正常退出:調用者的pc計數器的值作爲返回地址。異常退出:返回地址要通過異常表來確定,棧幀中一般不會保存這部分信息)。

本質:方法的退出就是當前棧幀出棧的過程。至此,需要恢復上層方法的局部變量表、操作數棧、將返回值壓入調用者棧幀的操作數棧、設置pc寄存器值等,讓調用者方法繼續執行下去。

4、動態鏈接

5、一些附加信息

4、本地方法棧

jvm虛擬機棧用於管理java方法的調用,而本地方法棧用於管理本地方法的調用。本地方法棧用c語言實現,是線程私有的。本地方法中通過鏈接到本地方法接口,來調用本地方法庫中的方法。當某個線程調用一個本地方法時,他就進入了一個全新的並且不再受虛擬機限制的世界,他和虛擬機擁有同樣的權限

5、程序計數器

作用:用來存儲指向下一條指令的地址,即即將要執行的指令代碼,由執行引擎讀取下一條指令。
同時也存儲當前方法的jvm指令地址,如果是在執行本地方法,則是未指定(undefned)。
爲了能夠準確的記錄各個線程正在執行的當前字節碼指令地址,最好的辦法是爲每一個線程都分配一個pc寄存器。

三、執行引擎

虛擬機的核心組件,負責執行虛擬機的字節碼,一般用戶先進行編譯成機器碼後再執行。

四、本地方法接口

本地方法接口用於向本地方法棧中提供本地方法(使用native關鍵字修飾的java方法,我們稱爲本地方法),是java方法調用本地方法的一個橋樑,同時也起到了融合不同的編程語言爲java所用。

一個Native Method就是一個java調用非java代碼的 接口。定義一個native method時,並不提供實現體(有些像定義一個java interface),因爲其實現體是由非java語言在外面實現的。

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