1. 程式人生 > >JVM(一):Run-Time Data Areas(執行時資料區)/ 記憶體區域

JVM(一):Run-Time Data Areas(執行時資料區)/ 記憶體區域

一:前言

特別說明:文章中引用的圖片是通過谷歌的方式找到的,當時並沒有找到圖片是否擁有版權,如果遇到了的話,請告知博主,我會將相應的圖片刪除。

本部落格主要總結的是JVM的Run Time Data Areas(執行時資料區),也就是我們常說的記憶體區域。借用Inside Java Virtual Machine裡的一句話來說明一下這一塊內容。

When a Java virtual machine runs a program, it needs memory to store many things, including bytecodes and other information it extracts from loaded class files, objects the program instantiates, parameters to methods, return values, local variables, and intermediate results of computations. The Java virtual machine organizes the memory it needs to execute a program into several runtime data areas.

上面這句話的意思是:執行時資料區就是儲存二進位制程式碼、類檔案資訊、程式物件的實力、方法引數、返回值、本地變數、計算結果等的一塊記憶體區域。
為了方便大家的閱讀,在後面的片段中,我會將執行時資料區稱為記憶體區域(畢竟這是大家習慣也更願意這樣說的稱呼,前者更像是JVM規範中的一種說法)。

二:結構

這裡寫圖片描述

如上所示就是JVM記憶體分佈的結構圖,在JVM規範中其實只說明瞭上半部分的構成,直接記憶體是Java加上NIO後才出現的一塊內容,具體的內容將在後續內容中說明。如果我們將每個區的內容再細化一下,那麼就會得到如下一章記憶體分佈圖。
JVM記憶體分佈(精細版)

三:內容詳解

3.1 程式計數器

屬性 內容
英文名稱 Programmer Counter Register / PC Register
作用 儲存當前執行緒執行的行號指示器(記憶體地址)
作用域 執行緒私有(每個執行緒都擁有一個這樣的程式計數器)
大小 一個字長
生命週期 與所處執行緒的生命週期一樣
儲存內容 1:如果執行的是Java方法,那麼將會記錄正在執行的位元組碼指令。2:如果執行的Native方法,那麼將會為空。3:Return Address.
異常 不丟擲異常
特別說明 JVM是通過執行緒輪流切換並分配處理器的執行時間的方式來實現的,所以需要一個能夠記錄各個執行緒執行的程式碼的位置的容器,也就是我們的程式計數器

3.2 Java虛擬機器棧

屬性 內容
英文名稱 Java Virtual Machine Stack / JVM Stack
作用 儲存方法在執行過程中所需的資料資訊;並且在方法的呼叫和返回上發揮著重要的作用
作用域 執行緒私有(每個執行緒都擁有一個這樣的程式計數器)
大小 可以通過配置,設定該棧的為固定大小也可以設定棧的最小最大值。
基本單位 棧幀(Stack Frame)
生命週期 與所處執行緒的生命週期一樣
儲存內容 儲存的基本單元是棧幀(Stack Frame),執行一個方法就會產生一個棧幀,棧幀就包含執行時所需的一些資料資訊。從概念上分,棧幀中包含區域性變量表(Local Variables)、運算元棧(Operand Stack)、動態連結(Dynamic Linking)、方法返回地址(Return Address)以及一些其它的資訊(例如我們的除錯資訊也會存在與這個部分)
異常 1:StackOutOfMemoryError發生在一個執行緒中當單個棧的限定記憶體不能夠滿足使用者在執行過程中所需的記憶體那麼就會丟擲該異常。2:OutOfMemoryError發生在當用戶建立多個執行緒的時候,由於每個執行緒都有最低記憶體設定,當我們的記憶體不能夠滿足我們建立指定個數的執行緒的時候就會丟擲記憶體溢位異常

3.2.1 本地變量表

屬性 內容
英文名稱 Local Variables
作用 儲存方法中的變數資訊
作用域 棧幀私有,不同的棧幀之間不可以相互呼叫
生命週期 呼叫方法時候建立,方法執行完畢銷燬
儲存內容 一個包含方法中變數的陣列;方法引數、方法變數
基本單位 變數槽(Variable Slot)
資料型別 1:一個導向槽可以儲存byte、boolean、char、short、int、float、reference、returnAddress。2:long、double需要兩個導向槽。
注意事項 1:對於非靜態變數方法,區域性變量表的第一個值儲存的是方法所在物件的引用(也就是我們常說的this);而對於靜態變數方法則沒有。2:區域性變量表的大小在編譯的時候已經確定,這個資訊就存在方法區中。3:byte、char、short會在儲存的時候被自動轉換為int型別。

3.2.2 運算元棧

屬性 內容
英文名稱 Operand Stack
作用 記錄方法在執行的過程中用到的變數
資料結構 先進後出的棧
生命週期 呼叫方法時候建立,方法執行完畢銷燬
儲存內容 記錄在方法執行過程中需要用到的變數的值,所有值的呼叫都是通過指令集將對應的變數新增到運算元棧,然後再呼叫
基本單位 變數槽(Variable Slot)
注意事項 1:大小在編譯的時候已經確定,這個資訊就存在方法區中。

3.2.3 動態連結

屬性 內容
英文名稱 Dynamic Linking
作用 一般的解析都是靜態解析,通過這個我們可以實現動態解析,當執行到某一塊程式碼的時候再進行解析執行
生命週期 呼叫方法時候建立,方法執行完畢銷燬
特別說明 由於每個棧幀對記錄的有一個當前棧幀在執行時常量池中的引用,所以可以通過這個引用幫我們實現動態連結相關的操作

3.2.4 方法返回地址

屬性 內容
英文名稱 Return Address
作用 記錄當前方法的執行結果
內容 1:當方法正常執行的時候,記錄的是一個代表執行結果的位元組碼,常被稱為Normal Method Invocation Completion。2:當方法出現異常的時候,返回的是一個來自異常表中的異常,這種常被稱為Abrupt Method Invocation Completion。

3.2.5 其它資訊

這其它資訊中比較常見的一項就是除錯(debugger)的資訊、常量池的引用資訊等。有些資訊根據虛擬機器的不同會有不同的變化。

3.2.6 結語

如果我們對這個進行分的的話,我們會發現,這個區域主要是記錄相應的內容,而並未涉及到相應的呼叫操作,而執行的具體操作也就是push和pop,所以從這點上來看,我們在實現這塊內容的時候,不僅可以使用棧的技術,也可以使用堆來建立。

3.3 Native 方法棧

在結構上和Java虛擬機器棧是一樣的,只是說呼叫的方法可能會不同,前者呼叫的都是Java方法,而Java虛擬機器棧則是呼叫的Native方法。由於結構都是一樣的這裡就不再作進一步的說明了。

3.4 Java堆

屬性 內容
英文名稱 Java Heap
作用 記錄所有的例項化物件的資訊以及陣列的資訊
作用域 所有執行緒共享
生命週期 在JVM開啟時建立,物件的堆記憶體由在GC的時候被銷燬,整個堆的消失實在JVM關閉的時候
大小 既可以固定大小也可以配置大小的上下線
注意事項 1:由於堆的記憶體是共享的,所以不同執行緒呼叫堆中相同的資訊,這也是鎖出現的原因。2:Java只提供了分配記憶體的命令,並沒有提供銷燬記憶體的命令,所以需要GC來控制
構成 1:如果使用的是分代演算法,那麼就分為新生代、老生代。具體來說就是:Eden空間、From Survivor空間、To Survivor空間
優化 雖然所示所有執行緒共享堆,但是可以通過做特定的操作,來優化GC的效率,比如說設定TLAB:Thread Local Allocation Buffer
異常 當用戶設定的容量不滿足程式所需的容量的時候,會丟擲OutOfMemoryError

3.5 方法區

屬性 內容
英文名稱 Method Area
作用 儲存每個類的結構
作用域 所有執行緒共享
生命週期 在JVM開啟時建立,部分內容在GC的時候被銷燬,整個的消失實在JVM關閉的時候
大小 既可以固定大小也可以配置大小的上下線
構成 執行時常量池、欄位資訊、方法資訊、所有的靜態變數、Class Loader的引用、Class的引用等
異常 當用戶設定的容量不滿足程式需求的容量的時候,會丟擲OutOfMemoryError

3.5.1 執行時常量池

屬性 內容
英文名稱 Run Time Constant Pool
作用 儲存編譯時期的字面量以及符號引用
作用域 所有執行緒共享
生命週期 在JVM開啟時建立,部分內容在GC的時候被銷燬,整個的消失實在JVM關閉的時候
大小 既可以固定大小也可以配置大小的上下線
注意事項 Java語言要求常量不一定在編譯期才能使用,執行的時候一樣也可以將新的常量放入常量池,比如String.intern()

3.5.2 型別資訊

屬性 內容
英文名稱 Type Information
作用 儲存類或者介面的相關資訊,比如:識別符號,父類或者父介面的全名,類的全名
作用域 所有執行緒共享
生命週期 在JVM開啟時建立,部分內容在GC的時候被銷燬,整個的消失實在JVM關閉的時候
大小 既可以固定大小也可以配置大小的上下線

3.5.3 欄位資訊

屬性 內容
英文名稱 Field Information
作用 記錄欄位的資料相關資訊:名字、型別、識別符號
作用域 所有執行緒共享

3.5.4 方法資訊

屬性 內容
英文名稱 Method Information
作用 記錄欄位的方法相關資訊
作用域 所有執行緒共享
儲存內容 1:方法的名字,識別符號,返回值。2:區域性變量表、運算元棧的大小。3:方法引數的型別和數量。4:方法的二進位制程式碼

3.5 直接記憶體

本地記憶體的出現是自Java中添加了NIO之後才有的,NIO引入了一個基於通道/快取的I/O方式,可以使用Native函式直接在對外分配記憶體,這樣就避免了Java堆和Native堆的相互呼叫了,提升了訪問速度。這塊,也是丟擲OutOfMemoryError異常的。