1. 程式人生 > >Java虛擬機器之記憶體區域

Java虛擬機器之記憶體區域

**原創文章,轉載請標明出處!https://www.cnblogs.com/boycelee/p/14095080.html** [toc] # 一、背景 相對於C/C++C程式設計師,Java程式設計師會相對輕鬆一些,因為Java虛擬機器的記憶體管理機制會管理記憶體,不需要開發人員手動進行記憶體管理,也不容易出現記憶體洩露和記憶體溢位的。但如果不瞭解虛擬機器如何管理記憶體,在記憶體出現問題時就會束手無策,所以學習虛擬機器如何管理記憶體也是一件必要的事情。 # 二、執行時記憶體區域概述 #### 1、官方描述 `The Java Virtual Machine defines various run-time data areas that are used during execution of a program.` #### 2、中文翻譯 Java虛擬機器定義了在程式執行期間的各種執行時資料區域。 #### 3、記憶體區域簡述 在《Java虛擬機器規範》中執行時資料區域會包括PC暫存器、Java虛擬機器棧、堆、方法區、執行常量池、本地方法棧。因為執行時常量池是方法區的一部分,所以本篇文章將常量池放在方法區章節中的子節來講解。 #### 4、執行時資料區簡圖 #### 5、執行時資料區詳圖 # 三、JVM執行緒 ## JVM資料區域與執行緒關係 #### 1、官方描述 `Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.` #### 2、中文解釋 `一部分資料區域與虛擬機器程序同生共死,另一部分資料區域與執行緒同生共死。` #### 3、關係圖
# 四、PC暫存器 #### 1、官方解釋 `The Java Virtual Machine can support many threads of execution at once (JLS §17). Each Java Virtual Machine thread has its own pc (program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method ([§2.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6)) for that thread. If that method is not native , the pc register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread is native, the value of the Java Virtual Machine's pc register is undefined. The Java Virtual Machine's pc register is wide enough to hold a returnAddress or a native pointer on the specific platform.` #### 2、中文翻譯 Java虛擬機器支援同時執行多個執行緒。每一個Java虛擬機器執行緒都有自己的PC暫存器。在任何時刻,Java虛擬機器一個執行緒都只在執行某一個單一函式程式碼。如果函式不是native函式,PC暫存器中就包含當前正在被執行的Java虛擬機器指令的地址。反之當前函式是native函式,pc暫存器中的值是undefined。pc暫存器的大小足夠儲存返回地址或native指標。 #### 3、概述 (1)PC暫存器並非真正意義上的物理暫存器,pc暫存器是對物理暫存器的一種模擬; (2)PC暫存器是一塊較小的記憶體空間; (3)可以將其看做當前執行緒執行的位元組碼指令的“行號指示器”; (4)位元組碼直譯器的工作就是改變pc暫存器的值來選取下一條需要執行的位元組碼指令; (5)PC暫存器的作用是儲存下一條指令地址,也就是即將要執行的指令程式碼,然後由執行引擎讀取下一條指令; (6)在Java虛擬機器規範中,PC暫存器是執行緒私有的,其生命週期與執行緒生命週期保持一致; (7)PC暫存器是Java虛擬機器規範中沒有規定任何OutOtMemoryError的區域。 #### 3、什麼是上下文切換? 當單核處理器執行多執行緒程式碼時,會為每個執行緒分配CPU時間片,CPU通過時間片分配演算法來迴圈執行任務,當前任務執行一個時間片後會切換到下一個任務。但是在切換前會儲存上一個任務的狀態,以便於下次切換回任務時,可以再次載入這個任務之前的狀態。所以**任務從儲存到再一次載入的過程就是一次“上下文切換”**。CPU通過不停進行上下文切換,讓我們覺得多個執行緒是同時執行的。 #### 4、什麼是CPU時間片? CPU時間片就是CPU分配給每個執行緒的時間段。由於CPU只有一個核數有限,只能同時處理程式的一部分任務,不能同時滿足所有要求,為了公平處理多執行緒問題,就引入的時間片的概念,為每個執行緒分配時間片,輪流執行。 #### 5、為什麼PC暫存器是“執行緒私有”的? 由於Java虛擬機器多執行緒是通過執行緒上下文切換的方式來實現的。在任何時刻,一個處理器只會執行一條程式中的指令,因此在上下文切換後為了能夠恢復到正確的執行位置,每個執行緒都需要有一個獨立的PC暫存器,執行緒之間獨立儲存,互不影響。 # 五、虛擬機器棧 #### 1、官方解釋 `Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames ([§2.6](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.6)). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous.` #### 2、中文解釋 Java虛擬機器棧是執行緒私有的,與執行緒同生共死。Java虛擬機器中有一個個儲存棧幀。Java的棧能夠儲存區域性變數與部分返回結果,並參與函式的呼叫與返回。因為Java虛擬機器棧只push或pop棧幀,不直接操作Java虛擬機器棧,棧幀可能被堆分配。Java虛擬機器棧的記憶體不需要連續。 #### 3、概述 Java虛擬機器棧描述的是Java函式執行的執行緒記憶體模型。每個函式被執行時,Java虛擬機器會同步建立一個棧幀用於儲存區域性變數標配、運算元棧、動態連結和函式返回地址等資訊。函式被呼叫直至執行完畢的過程,就對應著一個棧幀在虛擬機器中入棧和出棧的過程。 #### 4、棧的結構
#### 5、棧的儲存 (1)棧是執行緒私有,棧中的資料都已棧幀形式存在。棧幀是棧的基本單位; (2)執行緒中正在執行的函式都有其對應的棧幀; (3)棧幀是一個記憶體區塊,是一個數據集合,其中儲存著函式執行過程中的資料資訊; #### 6、棧的執行原理 (1)JVM直接對Java棧的操作只有兩個,就是對棧幀的壓棧和出棧。 (2)在同一執行緒同一時間下,只會有一個活動的棧幀,且該棧幀是當前正在執行的函式對應的棧幀,即棧頂棧幀也稱為“當前棧幀”。 (3)執行引擎執行的所有位元組碼指令只針對當前棧幀進行操作。 (4)若方法中呼叫了其他方法,對應的新的棧幀就會被建立,放在棧頂,成為新的當前棧幀。
#### 7、區域性變量表 **(1)概述** ​ 1)區域性變量表定義為一個數組,主要用於儲存方法引數和定義在方法體內的區域性變數。包括基本資料型別和物件引用以及returnAddress型別(指向特定指令地址的指標,例如pc暫存器中的值就是returnAddress型別)。 ​ 2)區域性變量表所需容量在編譯期間已經確定下下來,並儲存在方法的Code屬性的maximun local variables資料項中。在函式執行期區域性變量表大小不會改變。 **(2)舉例程式碼** ```java public class Car { public static void main(String[] args) { Car car = new Car(); String name = "Boyce Car"; } } ``` **(3)位元組碼檔案中的區域性變量表** ​ 1)start pc表示位元組碼指令的行號; ​ 2) length是pc指標起始位置到結束位置的距離; ​ 3) start pc與length共同描述變數的作用域範圍。 **(4)Slot** ​ 1)區域性變量表的最基本儲存單元Slot(變數槽) ​ 2)在區域性變量表中,32位以內的型別只佔一個Slot(包括returnAddress型別),64位型別(long和duble) 佔兩個Slot。 ​ 3)區域性變量表中,每一個Slot都會分配到一個訪問索引,通過這個索引可以訪問到區域性變量表中對應的區域性變 量值。 ​ 4)當一個例項方法被呼叫時,它的方法引數和方法體內定義的區域性變數將會按照順序被複制到區域性變量表中 的Slot上。 ​ 5)當需要訪問區域性變量表中的64位的區域性變數值時,只需要使用2個Slot索引中的前一個索引即可。 ​ 6)如果當前幀由建構函式或例項方法建立的,那麼該物件引用this將存放在index為0的Slot處。 #### 8、運算元棧 (1)每個棧幀都包含一個先進後出的運算元棧。 (2)運算元棧在方法執行過程中,根據位元組碼指令,往棧中寫入或讀取資料,即入棧或出棧。 (3)運算元棧的作用主要是用於儲存計算過程的中間結果,同時作為計算過程中變數的臨時儲存空間。 (4)運算元棧根據push/pop進行操作,無法使用索引方式訪問。 (5)如果一個函式帶有返回值,其返回值會被壓入當前棧幀的運算元棧中,並更新pc暫存器中的下一條位元組碼指令。 (6)Java虛擬機器的指令架構是基於棧式架構,其中的棧指的就是運算元棧。 (7)基於棧式結構計算過程的位元組碼指令: #### 9、棧頂快取 (1)運算元棧儲存於記憶體,頻繁操作進行IO操作影響執行效率。HotSpot虛擬機器的設計者提出了棧頂快取技術,將棧頂元素快取在暫存器中,以此減少IO操作,提升執行效率。(處理器訪問任何暫存器和 Cache 等封裝以外的資料資源都可以當成 I/O 操作,包括記憶體,磁碟,顯示卡等外部裝置。) #### 10、動態連結 (1)每個棧幀內部都包含一個紙箱執行時常量池中該棧幀所屬方法的引用。包含這個引用的目的就是為了支援當前程式碼能夠實現動態連結(Dynamic Linking)。例如:invokedynamic指令。 (2)Java原始檔被編譯成位元組碼檔案時,字面量與符號引用都被儲存至位元組碼檔案的常量池中。例如:當一個函式呼叫另一個函式時,就通過常量池中指向的函式的符號引用來表示,動態連結的作用就是將這些符號引用轉換為呼叫函式的直接引用(函式在實際執行時記憶體中的入口地址)。 #### 11、方法返回地址 (1)存放呼叫該函式的主函式的pc暫存器的值; (2)一個函式的結束有兩種方式:1)正常執行完成;2)異常,非正常退出; (3)無論通過哪種方式退出,在函式退出後都返回到該函式被呼叫的位置,程式才能繼續執行。正常退出時,呼叫方的pc暫存器的值作為返回地址,即呼叫該方法的指令的下一條指令的地址。而如果通過異常退出,返回地址是要通過異常表來確定,棧幀中一般不會儲存這部分資訊。 # 六、本地方法棧 #### 1、官方解釋 `An implementation of the Java Virtual Machine may use conventional stacks, colloquially called "C stacks," to support native methods (methods written in a language other than the Java programming language). Native method stacks may also be used by the implementation of an interpreter for the Java Virtual Machine's instruction set in a language such as C. Java Virtual Machine implementations that cannot load native methods and that do not themselves rely on conventional stacks need not supply native method stacks. If supplied, native method stacks are typically allocated per thread when each thread is created.` #### 2、中文翻譯 Java虛擬機器的實現可以使用傳統的堆疊,以支援本地方法,本地方法棧也可以用於實現Java虛擬機器指令集的直譯器。Java虛擬機器不能載入本地方法且不提供本地方法棧。如果提供本地方法棧,則執行緒建立時為每個執行緒分配一個本地方法棧。 #### 3、概述 (1)本地方法棧與Java虛擬機器棧相似,Java虛擬機器棧用於管理Java函式的執行問題,而本地方法棧則是用於管理本地函式(Native)的執行問題。 (2)《Java虛擬機器規範》中對本地方法棧沒有強制規定,因此虛擬機器可以根據需求自由實現。如Hot-Spot虛擬機器就直接將本地方法棧和虛擬機器棧合二為一。 (3)棧深度溢位或棧擴充套件失敗時會分配丟擲StackOverflowError和OutOfMemoryError異常。 (4)當執行緒呼叫本地方式時,它和虛擬機器就有相同的許可權,不再受虛擬機器的限制。 #### 4、程式碼示例 在安卓開發時,我們需要呼叫C/C++程式碼,我們就需要用到JNI(Java Native Interface)。 ```java package com.example.nativetest1; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); } ``` ```c++ #