天天看點

Java 8 的記憶體結構

Java8記憶體結構圖

Java 8 的記憶體結構

虛拟機記憶體與本地記憶體的差別

Java虛拟機在執行的時候會把管理的記憶體配置設定成不同的區域,這些區域被稱為虛拟機記憶體,同時,對于虛拟機沒有直接管理的實體記憶體,也有一定的利用,這些被利用卻不在虛拟機記憶體資料區的記憶體,我們稱它為本地記憶體,這兩種記憶體有一定的差別:

JVM記憶體

  • 受虛拟機記憶體大小的參數控制,當大小超過參數設定的大小時就會報OOM

本地記憶體

  • 本地記憶體不受虛拟機記憶體參數的限制,隻受實體記憶體容量的限制
  • 雖然不受參數的限制,但是如果記憶體的占用超出實體記憶體的大小,同樣也會報OOM

java運作時資料區域

java虛拟機在執行過程中會将所管理的記憶體劃分為不同的區域,有的随着線程産生和消失,有的随着java程序産生和消失。

程式計數器(Program Counter Register)

程式計數器就是目前線程所執行的位元組碼的行号訓示器,通過改變計數器的值,來選取下一行指令,通過他來實作跳轉、循環、恢複線程等功能。

  • 在任何時刻,一個處理器核心隻能運作一個線程,多線程是通過線程輪流切換,配置設定時間來完成的,這就需要有一個标志來記住每個線程執行到了哪裡,這裡便需要到了程式計數器。
  • 是以,程式計數器是線程私有的,每個線程都有自己的程式計數器。

虛拟機棧(JVM Stacks)

Java 8 的記憶體結構

 虛拟機棧是線程私有的,随線程生滅。虛拟機棧描述的是線程中的方法的記憶體模型:

每個方法被執行的時候,都會在虛拟機棧中同步建立一個棧幀(stack frame)。

每個棧幀的包含如下的内容

  • 局部變量表
    • 局部變量表中存儲着方法裡的java基本資料類型(byte/boolean/char/int/long/double/float/short)以及對象的引用(注:這裡的基本資料類型指的是方法内的局部變量)
  • 操作數棧
  • 動态連接配接
  • 方法傳回位址

方法被執行時入棧,執行完後出棧

虛拟機棧可能會抛出兩種異常:

  • 如果線程請求的棧深度大于虛拟機所規定的棧深度,則會抛出StackOverFlowError即棧溢出
  • 如果虛拟機的棧容量可以動态擴充,那麼當虛拟機棧申請不到記憶體時會抛出OutOfMemoryError即OOM記憶體溢出

本地方法棧(Native Method Stacks)

本地方法棧與虛拟機棧的作用是相似的,都會抛出OutOfMemoryError和StackOverFlowError,都是線程私有的,主要的差別在于:

  • 虛拟機棧執行的是java方法
  • 本地方法棧執行的是native方法

Java堆(Java Heap)

java堆是JVM記憶體中最大的一塊,由所有線程共享,是由垃圾收集器管理的記憶體區域,主要存放對象執行個體,當然由于java虛拟機的發展,堆中也多了許多東西,現在主要有:

  • 對象執行個體
    • 類初始化生成的對象
    • 基本資料類型的數組也是對象執行個體
  • 字元串常量池
    • 字元串常量池原本存放于方法區,jdk7開始放置于堆中。
    • 字元串常量池存儲的是string對象的直接引用,而不是直接存放的對象,是一張string table
  • 靜态變量
    • 靜态變量是有static修飾的變量,jdk7時從方法區遷移至堆中
  • 線程配置設定緩沖區(Thread Local Allocation Buffer)
    • 線程私有,但是不影響java堆的共性
    • 增加線程配置設定緩沖區是為了提升對象配置設定時的效率

java堆既可以是固定大小的,也可以是可擴充的(通過參數-Xmx和-Xms設定),如果堆無法擴充或者無法配置設定記憶體時也會報OOM。

方法區(Method Area)

方法區是所有線程共享的記憶體,在java8以前是放在JVM記憶體中的,由永久代實作,受JVM記憶體大小參數的限制,在java8中移除了永久代的内容,方法區由元空間(Meta Space)實作,并直接放到了本地記憶體中,不受JVM參數的限制(當然,如果實體記憶體被占滿了,方法區也會報OOM),并且将原來放在方法區的字元串常量池和靜态變量都轉移到了Java堆中,方法區與其他區域不同的地方在于,方法區在編譯期間和類加載完成後的内容有少許不同,不過總的來說分為這兩部分:

類元資訊(Klass)

  • 類元資訊在類編譯期間放入方法區,裡面放置了類的基本資訊,包括類的版本、字段、方法、接口以及常量池表(Constant Pool Table)
  • 常量池表(Constant Pool Table)存儲了類在編譯期間生成的字面量、符号引用
  • 運作時常量池主要存放在類加載後被解析的字面量與符号引用,但不止這些
  • 運作時常量池具備動态性,可以添加資料,比較多的使用就是String類的intern()方法

直接記憶體

Native方法