運行時數據區域
程序計數器
- 程序計數器是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指令器。
- 在JAVA的概念模型中,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,它是程序控制流的指示器,分支,循環,跳轉,異常處理,線程恢復等基礎功能都需要依賴這個計數器來完成。
-
線程私有:
JAVA
虛擬機的多線程都是通過輪流切換,分配處理器執行時間的方式來實現的。在任何一個時刻,一個處理器都只會執行一條線程中的指令。因爲,爲了線程切換後能夠恢復到正確的執行位置,每個線程都需要一個獨立的程序計數器,各個線程之前計數器互不影響,獨立存儲,我們稱這類內存爲線程私有的內存。 -
如果線程正在執行的是
JAVA
方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是本地(Native
)方法,這個計數器則爲空。 -
此內存區域是唯一一個沒有在《JAVA虛擬機規範》中沒有規定任何
OutOfMemoryError
情況的區域。
虛擬機棧
- 線程私有,生命週期和線程相同。虛擬機棧描述的是JAVA方法執行的線程內存模型:每個方法執行的時候,虛擬機都會同步創建一個棧幀用於存儲局部變量表,操作數棧,動態連接,方法出口等信息。每個方法被調用直至執行完畢的過程,就對應一個棧幀在虛擬機從入棧到出棧的過程。
- 局部變量表存放了編譯器可知的各種
JAVA
基本數據類型和對象引用類型。
- 對象引用類型(
reference
類型),並不等同於對象本身,可能是指向對象起始地址的引用指針,也有可能是指向一個代表對象的句柄或者其他與此對象相關的位置。 rerurnAddress
類型。指向了一條字節碼指令的地址。- 數據類型在局部變量表中以局部變量槽來表示,其中64位的
long
和double
會佔據倆個變量槽。 - 局部變量表所需的內存空間在編譯期間完成分配,在方法期間不會改變局部變量表的大小。大小指的是槽的數量。
- 在《虛擬機規範》中,對這塊內存區域定義了倆種異常。
- 如果線程請求的深度大於虛擬機允許的深度,
StackOverFlowError
- 如果虛擬機棧容量可以動態擴展,當棧無法申請到足夠的內存。
OutOfMemoryError
- 如果線程請求的深度大於虛擬機允許的深度,
JAVA堆
- 堆是虛擬機所管理的內存中最大的一塊區域,被所有線程共享。在啓動時創建,唯一目的是存放對象實例,幾乎所有的對象實例都在這裏分配內存。
- 堆是垃圾處理器管理的內存區域,因此也被成爲GC堆。
- 堆可以處於物理上的不連續的內存空間中,但在邏輯上它應該被視爲是連續的。
- 堆的大小可以固定,可以動態擴展。(
-Xmx
和-Xms
)。 - 堆如果沒有完成內存實例分配,並且堆也無法再擴展時。
OutOfMemoryError
方法區
- 同堆一樣,線程共享的內存區域,用於存儲已被虛擬機加載的類型信息,常量,靜態變量,即時編譯器編譯後的代碼緩存等數據。
- 永久代的概念在jdk8之後,移到了元空間。
- 垃圾回收行爲在這塊區域出現的較少,內存回收目標主要是針對常量池的回收和對類型的卸載。
- 如果方法區無法滿足新的內存分配需求時。
OutOfMemoryError
運行時常量池
- 是方法區的一部分。Class文件中除了有類的版本,字段,方法,接口等描述信息外,還有一項信息是常量池表,用於存放編譯期生成的各種字面量與符號引用,這部分內容將在類加載後存放到方法區的運行時常量池中。
- 運行時常量池相對於Class文件常量池的另外一個重要特徵就是具有動態性,JAVA語言並不要求常量一定只有編譯器才能產生,運行期間也可以將新的常量放入池中,這種特徵被開發人員利用比較多的便是
String
類的intern()
方法。
- 當常量池無法再申請到內存時。OutOfMemoryError
直接內存
JDK1.4
引入了NIO
的概念,這種是基於管道的I/O
方式,它可以使用Native
函數直接分配堆外內存,然後通過一個存儲在JAVA堆
裏面的DirectByteBuffer
對象作爲這塊內存的引用進行操作。能夠在一些場景中顯著提高性能,避免了在JAVA
堆和Native
堆來回複製數據。- 不會受到虛擬機內存影響,但受到總內存影響,如果各區域內存大於總內存,
OutOfMemoryError
。