JVM系列-01-JVM內存模型

[TOC]java

聲明

本篇文章是本人閱讀《深刻理解JVM》和《java虛擬機規範》時的筆記。
JVM是HotSpot,jdk1.7。
大神繞路,不喜勿噴。程序員

1 JVM內存模型

JVM內存模型

2 程序計數器(PC)

每一個線程都會有本身私有的程序計數器(PC)。能夠看做是當前線程所執行的字節碼的行號指示器。
也能夠理解爲下一條將要執行的指令的地址或者行號。字節碼解釋器就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、 循環、 跳轉、 異常處理、 線程上下文切換,線程恢復時,都要依賴PC.數組

  • 若是線程正在執行的是一個Java方法,PC值爲正在執行的虛擬機字節碼指令的地址數據結構

  • 若是線程正在執行的是Native方法,PC值爲空(未定義)函數

JVM內存模型

說白了,PC就是一塊內存區域。存放着下一條要執行的指令的地址。spa

3 虛擬機棧(VM Stack)

3.1 簡介

VM Stack也是線程私有的區域。他是java方法執行時的字典:它裏面記錄了局部變量表、 操做數棧、 動態連接、 方法出口等信息。操作系統

在《java虛擬機規範》一書中對這部分的描述以下:線程

棧幀( Frame)是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態連接 (Dynamic Linking)、 方法返回值和異常分派( Dispatch Exception)。
棧幀隨着方法調用而建立,隨着方法結束而銷燬——不管方法是正常完成仍是異常完成(拋出了在方法內未被捕獲的異常)都算做方法結束。
棧幀的存儲空間分配在 Java 虛擬機棧( §2.5.5)之中,每個棧幀都有本身的局部變量表( Local Variables, §2.6.1)、操做數棧( OperandStack, §2.6.2)和指向當前方法所屬的類的運行時常量池( §2.5.5)的引用。code

VM-Stack

說白了,VM Stack是一個,也是一塊內存區域
因此,他是有大小的。雖然有大小,可是通常而言,各類虛擬機的實現都支持動態擴展這部份內存。對象

  • 若是線程請求的棧深度太大,則拋出StackOverflowError

  • 若是動態擴展時沒有足夠的大小,則拋出OutOfMemoryError

3.2 StackOverflowError

如下代碼確定會致使StackOverflowError:

public static void m1() {
    m1();
}

public static void main(String[] args) {
    m1();
}
Exception in thread "main" java.lang.StackOverflowError
    at xxx.xxx.xxx.m1(JavaVMStackSOF.java:10)

4 本地方法棧(Native Method Stack)

Java 虛擬機實現可能會使用到傳統的棧(一般稱之爲「 C Stacks」)來支持 native 方法( 指使用 Java 之外的其餘語言編寫的方法)的執行,這個棧就是本地方法棧( Native MethodStack)。

VM Stack是爲執行java方法服務的,此處的Native Method Stack是爲執行本地方法服務的。
此處的本地方法指定是和具體的底層操做系統層面相關的接口調用了(這部分過高高級了,不想深究……)。

《java虛擬機規範》中沒有對這部分作具體的規定。因此就由VM的實現者自由發揮了。
有的虛擬機(好比HotSpot)將VM Stack和Native Method Stack合二爲一,因此VM的另外一種內存區域圖就以下面所示了:

JVM內存模型-虛擬機棧和本地方法棧合二爲一

5 Java堆(Heap)

5.1 簡介

在 Java 虛擬機中,堆( Heap)是可供各條線程共享的運行時內存區域,也是供全部類實例和數組對象分配內存的區域。

如下是本人對《java虛擬機規範》一書中對Java堆的介紹的總結:

  • 在虛擬機啓動的時候就被建立

  • 是全部線程共享的內存區域

  • 存儲了被自動內存管理系統所管理的各類對象

    • 這些受管理的對象無需,也沒法顯式地被銷燬

    • 自動內存管理系統:Automatic StorageManagement System,也便是常說的"Garbage Collector(垃圾收集器)"

    • 並未指明用什麼具體的技術去實現自動內存管理系統

  • Java 堆的容量能夠是固定大小的,也能夠隨着程序執行的需求動態擴展,並在不須要過多空間時自動收縮

  • Java 堆所使用的內存不須要保證是連續的

  • 若是實際所需的堆超過了自動內存管理系統能提供的最大容量,那 Java 虛擬機將會拋出一個OutOfMemoryError 異常

  • 實現者應當提供給程序員或者最終用戶調節 Java 堆初始容量的手段

    • 對於能夠動態擴展和收縮 Java 堆來講,則應當提供調節其最大、最小容量的手段

  • 全部的對象實例以及數組都要在堆上分配

JVM-Heap

至於堆內存的詳細狀況,將在後續的GC相關文章中介紹。

5.2 堆內存中的OutOfMemoryError

如下示例代碼確定致使堆內存溢出:

public static void main(String[] args) {
    ArrayList<Integer> list = Lists.newArrayList();
    while (true) {
        list.add(1);
    }
}

無限制的往list中添加元素,不管你的堆內存分配的多大,都會有溢出的時候。

java.lang.OutOfMemoryError: Java heap space

6 方法區(Method Area)

方法區是由全部線程共享的內存區域。
方法區存儲的大體內容以下:

  • 每個類的結構信息

    • 運行時常量池( Runtime Constant Pool)

    • 字段和方法數據

    • 構造函數和普通方法的字節碼內容

  • 類、實例、接口初始化時用到的特殊方法

如下是本人對《java虛擬機規範》一書中對方法區的介紹的總結:

  • 在虛擬機啓動的時候被建立

  • 雖然方法區是堆的邏輯組成部分,可是簡單的虛擬機實現能夠選擇在這個區域不實現垃圾收集

  • 不限定實現方法區的內存位置和編譯代碼的管理策略

  • 容量能夠是固定大小的,也能夠隨着程序執行的需求動態擴展,並在不須要過多空間時自動收縮。

  • 方法區在實際內存空間中能夠是不連續的

  • Java 虛擬機實現應當提供給程序員或者最終用戶調節方法區初始容量的手段

    • 對於能夠動態擴展和收縮方法區來講,則應當提供調節其最大、最小容量的手段

  • 若是方法區的內存空間不能知足內存分配請求,那 Java 虛擬機將拋出一個OutOfMemoryError 異常

6.1 運行時常量池(Runtime Constant Pool)

《java虛擬機規範》中對常量池的介紹:

運行時常量池( Runtime Constant Pool)是每個類或接口的常量池( Constant_Pool,§4.4)的運行時表示形式,它包括了若干種不一樣的常量:從編譯期可知的數值字面量到必須運行期解析後才能得到的方法或字段引用。
運行時常量池扮演了相似傳統語言中符號表( SymbolTable)的角色,不過它存儲數據範圍比一般意義上的符號表要更爲普遍。

運行時常量池

每個運行時常量池都分配在 Java 虛擬機的方法區之中,在類和接口被加載到虛擬機後,對應的運行時常量池就被建立出來。

  • 當建立類或接口的時候,若是構造運行時常量池所須要的內存空間超過了方法區所能提供的最大值,那 Java 虛擬機將會拋出一個 OutOfMemoryError 異常。

7 直接內存(Direct Memory)

此處的直接內存並非由JVM管理的內存。他是利用本地方法庫直接在java堆以外申請的內存區域。
好比NIO中的DirectByteBuffer就是操做直接內存的。

直接內存的好處就是避免了在java堆和native堆直接同步數據的步驟。可是他並非由JVM來管理的。

固然,這部份內存區域的操做也可能會拋出OutOfMemoryError

參考文章

  • 《深刻理解JVM》

  • 《Java虛擬機規範》-JDK1.7

相關文章
相關標籤/搜索