Java-JVM 運行時內存結構(Run-Time Data Areas)

Java 虛擬機定義了在程序執行期間使用的各類運行時數據區域。html

其中一些數據區域全部線程共享,在 Java 虛擬機(JVM)啓動時建立,僅在 Java 虛擬機退出時銷燬。java

還有一些數據區域是每一個線程的。線程數據區域是在線程啓動時建立,線程結束時銷燬。git

 

1、運行時數據區劃分(JDK8)

一、The pc Register(PC 寄存器、程序計數器)github

二、Java Virtual Machine Stacks(Java 虛擬機棧、Java 棧)編程

三、Native Method Stacks(本地方法棧,C棧)數組

四、Heap(堆)oracle

五、Method Area(方法區,JDK8 中的實現叫元數據區(本地內存中),JDK7 中的實現叫永久代(JVM中))jvm

六、Run-Time Constant Pool(運行時常量池,方法區的一部分)編程語言

 

2、區劃分詳情

2.1.The PC Register(PC 寄存器)

每一個 JVM 線程都有本身的 pc 寄存器(內存爲線程私有,隨着線程的建立而建立,線程的結束而銷燬)。函數

在任什麼時候候,每一個 JVM 線程都在執行單個方法的代碼,即該線程的當前方法(字節碼解釋器經過改變程序計數器來選取下一條須要執行指令,從而實現代碼的流程控制,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成)。

若是該方法不是 native,則 pc 寄存器包含當前正在執行的 JVM 指令的地址(線程切換就知道上次線程執行到哪了)。

若是該方法是 native,則 pc 寄存器爲 Undefined(不會 OutOfMemoryError)。

 

2.2.Java Virtual Machine Stacks(Java 虛擬機棧)

描述 Java 方法執行的內存模型。

每一個 JVM 線程都有一個私有 JVM 棧,與線程同時建立(內存爲線程私有,隨着線程的建立而建立,線程的結束而銷燬)。

JVM 棧存儲 frames (棧幀)。方法調用和返回對應壓棧和出棧(棧頂的棧幀是當前正在執行的活動棧,也就是當前正在執行的方法,PC 寄存器也會指向這個地址,只有這個活動的棧幀的本地變量能夠被操做數棧使用)。

因爲除了壓棧和出棧以外,永遠不會直接操做 JVM 棧,JVM 棧的內存不須要是連續的。

JVM 規範容許 JVM 棧具備固定大小,也能夠根據計算的須要動態擴展和收縮(經過 -Xss 控制)。 

如下異常與 JVM 棧有關:

若是不能夠動態擴展 Java 虛擬機棧,當線程中的方法調深度用超過 Java 虛擬機棧最大深度時,會拋出 StackOverflowError 異常(出現 StackOverFlowError 時,內存空間可能還有不少)。

若是能夠動態擴展 Java 虛擬機棧,當線程嘗試進行擴展但可以使內存不足以實現擴展,或者可以使內存不足覺得新線程建立初始 Java 虛擬機堆棧時,會拋出 OutOfMemoryError 異常。

 

2.3.Native Method Stacks(本地方法棧)

描述本地方法運行過程的內存模型。

JVM 可使用常規棧來支持 native 方法(用 Java 編程語言之外的語言編寫的方法,執行也會建立棧幀)。

沒法加載 native 方法,而且自己不依賴於傳統堆棧的 JVM, 不須要提供本地方法棧。若是提供,則一般在每一個線程建立時分配本地方法棧。

本地方法棧具備固定大小,也能夠根據計算的須要動態擴展和收縮。

如下異常與本地方法棧有關:

若是不能夠動態擴展本地方法棧,當線程中的計算須要比容許的本地方法棧更大,則會拋出 StackOverflowError 異常。

若是能夠動態擴展本地方法棧,當嘗試進行本地方法棧擴展,但可以使內存不足,或沒有足夠的內存可用於爲當前前程建立初始本地方法棧,則會拋出 OutOfMemoryError 異常。

 

2.4.Heap(堆)

堆是運行時數據區,從中分配全部類實例和數組的內存(JVM 中內存最大的一塊,被全部線程共享,須要注意同步問題)。

堆是在 JVM 啓動時建立的。

堆中對象的存儲由垃圾收集器(GC,自動存儲管理系統回收,對象永遠不會被顯式釋放。

JVM 沒有特定類型的 GC,能夠根據實現者的系統要求選擇存儲管理技術。

堆能夠具備固定大小,也能夠根據計算的須要進行擴展(經過 -Xmx 和 -Xms 控制)。堆的內存不須要是連續的。

如下異常狀況與堆有關:

若是計算須要的堆量超過自動存儲管理系統可用的堆,則會拋出 OutOfMemoryError 異常。

 

2.5.Method Area(方法區)

方法區在全部 JVM 線程之間共享。方法區是在 JVM 啓動時建立的。方法區在邏輯上是堆的一部分,但可選擇不實現垃圾回收。

方法區存儲類結構,如運行時常量(Run-Time Constant Pool),字段和方法數據,以及方法和構造函數的代碼,包括類和實例初始化以及接口初始化中使用的特殊方法

JVM 規範未規定方法區的位置或用於管理編譯代碼的策略。

方法區能夠是固定大小的,也能夠根據計算的須要進行擴展。方法區的內存不須要是連續的。

如下異常與方法區有關:

若是方法區域中的內存沒法知足分配請求,會拋出 OutOfMemoryError 異常。

 

2.6.Run-Time Constant Pool(運行時常量池)

運行時常量池是方法區的一部分。

Class 文件中的常量池(constant_pool Table),用於存放編譯期生成的各類字面量和符號引用,這部分在類加載後進入方法區的運行時常量池中。

在運行期間,也能夠向常量池中添加新的常量。如 String 類的 intern() 方法。

每一個運行時常量池都是從 JVM 的方法區分配的

如下異常與類或接口的運行時常量池的構造有關:

在建立類或接口時,若是運行時常量池的構造須要的內存比 JVM 方法區中可用的內存多,會拋出 OutOfMemoryError 異常

 

3、直接內存(堆外內存)

不是 JVM 運行時數據區的一部分,但這部份內存也會被頻繁的使用,也可能致使 OutOfMemoryError 異常。

JDK 1.4 中新加入了 NIO 類,經過調用本地方法直接分配 Java 虛擬機以外的內存,而後經過一個存儲在堆中的 DirectByteBuffer 對象直接操做該內存。

避免了 Java 堆和 Native 堆來回交換數據的時間,更高效。

 

4、大概劃分圖(JDK8-HotSpot)


https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

http://www.hollischuang.com/archives/2509

https://github.com/doocs/jvm/blob/master/docs/01-jvm-memory-structure.md

http://www.javashuo.com/article/p-pmycbtot-ey.html

相關文章
相關標籤/搜索