Java 虛擬機定義了在程序執行期間使用的各類運行時數據區域。html
其中一些數據區域全部線程共享,在 Java 虛擬機(JVM)啓動時建立,僅在 Java 虛擬機退出時銷燬。java
還有一些數據區域是每一個線程的。線程數據區域是在線程啓動時建立,線程結束時銷燬。git
一、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(運行時常量池,方法區的一部分)編程語言
每一個 JVM 線程都有本身的 pc 寄存器(內存爲線程私有,隨着線程的建立而建立,線程的結束而銷燬)。函數
在任什麼時候候,每一個 JVM 線程都在執行單個方法的代碼,即該線程的當前方法(字節碼解釋器經過改變程序計數器來選取下一條須要執行指令,從而實現代碼的流程控制,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成)。
若是該方法不是 native,則 pc 寄存器包含當前正在執行的 JVM 指令的地址(線程切換就知道上次線程執行到哪了)。
若是該方法是 native,則 pc 寄存器爲 Undefined(不會 OutOfMemoryError)。
描述 Java 方法執行的內存模型。
每一個 JVM 線程都有一個私有 JVM 棧,與線程同時建立(內存爲線程私有,隨着線程的建立而建立,線程的結束而銷燬)。
JVM 棧存儲 frames (棧幀)。方法調用和返回對應壓棧和出棧(棧頂的棧幀是當前正在執行的活動棧,也就是當前正在執行的方法,PC 寄存器也會指向這個地址,只有這個活動的棧幀的本地變量能夠被操做數棧使用)。
因爲除了壓棧和出棧以外,永遠不會直接操做 JVM 棧,JVM 棧的內存不須要是連續的。
JVM 規範容許 JVM 棧具備固定大小,也能夠根據計算的須要動態擴展和收縮(經過 -Xss 控制)。
如下異常與 JVM 棧有關:
若是不能夠動態擴展 Java 虛擬機棧,當線程中的方法調深度用超過 Java 虛擬機棧最大深度時,會拋出 StackOverflowError 異常(出現 StackOverFlowError 時,內存空間可能還有不少)。
若是能夠動態擴展 Java 虛擬機棧,當線程嘗試進行擴展但可以使內存不足以實現擴展,或者可以使內存不足覺得新線程建立初始 Java 虛擬機堆棧時,會拋出 OutOfMemoryError 異常。
描述本地方法運行過程的內存模型。
JVM 可使用常規棧來支持 native 方法(用 Java 編程語言之外的語言編寫的方法,執行也會建立棧幀)。
沒法加載 native 方法,而且自己不依賴於傳統堆棧的 JVM, 不須要提供本地方法棧。若是提供,則一般在每一個線程建立時分配本地方法棧。
本地方法棧具備固定大小,也能夠根據計算的須要動態擴展和收縮。
如下異常與本地方法棧有關:
若是不能夠動態擴展本地方法棧,當線程中的計算須要比容許的本地方法棧更大,則會拋出 StackOverflowError 異常。
若是能夠動態擴展本地方法棧,當嘗試進行本地方法棧擴展,但可以使內存不足,或沒有足夠的內存可用於爲當前前程建立初始本地方法棧,則會拋出 OutOfMemoryError 異常。
堆是運行時數據區,從中分配全部類實例和數組的內存(JVM 中內存最大的一塊,被全部線程共享,須要注意同步問題)。
堆是在 JVM 啓動時建立的。
堆中對象的存儲由垃圾收集器(GC,自動存儲管理系統)回收,對象永遠不會被顯式釋放。
JVM 沒有特定類型的 GC,能夠根據實現者的系統要求選擇存儲管理技術。
堆能夠具備固定大小,也能夠根據計算的須要進行擴展(經過 -Xmx 和 -Xms 控制)。堆的內存不須要是連續的。
如下異常狀況與堆有關:
若是計算須要的堆量超過自動存儲管理系統可用的堆,則會拋出 OutOfMemoryError 異常。
方法區在全部 JVM 線程之間共享。方法區是在 JVM 啓動時建立的。方法區在邏輯上是堆的一部分,但可選擇不實現垃圾回收。
方法區存儲類結構,如運行時常量(Run-Time Constant Pool),字段和方法數據,以及方法和構造函數的代碼,包括類和實例初始化以及接口初始化中使用的特殊方法。
JVM 規範未規定方法區的位置或用於管理編譯代碼的策略。
方法區能夠是固定大小的,也能夠根據計算的須要進行擴展。方法區的內存不須要是連續的。
如下異常與方法區有關:
若是方法區域中的內存沒法知足分配請求,會拋出 OutOfMemoryError 異常。
運行時常量池是方法區的一部分。
Class 文件中的常量池(constant_pool Table),用於存放編譯期生成的各類字面量和符號引用,這部分在類加載後進入方法區的運行時常量池中。
在運行期間,也能夠向常量池中添加新的常量。如 String 類的 intern() 方法。
每一個運行時常量池都是從 JVM 的方法區中分配的。
如下異常與類或接口的運行時常量池的構造有關:
在建立類或接口時,若是運行時常量池的構造須要的內存比 JVM 方法區中可用的內存多,會拋出 OutOfMemoryError 異常。
不是 JVM 運行時數據區的一部分,但這部份內存也會被頻繁的使用,也可能致使 OutOfMemoryError 異常。
JDK 1.4 中新加入了 NIO 類,經過調用本地方法直接分配 Java 虛擬機以外的內存,而後經過一個存儲在堆中的 DirectByteBuffer 對象直接操做該內存。
避免了 Java 堆和 Native 堆來回交換數據的時間,更高效。
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