Yige

Yige

Build

JVM系列-JVM記憶體結構

JVM 系列 - JVM 內存結構#

內容來自:

  1. JVM 內存結構
  2. JVM 內存結構和 Java 內存模型

基本概念#

Java 虛擬機的內存空間分為 5 個部分:

  • 程序計數器
  • Java 虛擬機棧
  • 本地方法棧
  • 方法區

引用《深入理解 Java 虛擬機》一書中的圖:
image.png

程序計數器 (PC 寄存器)#

程序計數器的定義#

程序計數器是一塊較小的內存空間,是當前線程正在執行的那條字節碼指令的地址。若當前線程正在執行的是一個本地方法,那麼此時程序計數器為Undefined

程序計數器的作用#

  • 字節碼解釋器通過改變程序計數器來依次讀取指令,從而實現代碼的流程控制。
  • 在多線程情況下,程序計數器記錄的是當前線程執行的位置,從而當線程切換回來時,就知道上次線程執行到哪了

程序計數器的特點#

  • 是一塊較小的內存空間。
  • 線程私有,每條線程都有自己的程序計數器。
  • 生命周期:隨著線程的創建而創建,隨著線程的結束而銷毀。
  • 是唯一一個不會出現 OutOfMemoryError 的內存區域

Java 虛擬機棧#

Java 虛擬機棧的定義#

Java 虛擬機棧是描述 Java 方法運行過程的內存模型。

Java 虛擬機棧會為每一個即將運行的 Java 方法創建一塊叫做 “棧幀” 的區域,用於存放該方法運行過程中的一些信息,如圖所示:
image.png

Java 虛擬機棧的特點#

  • 局部變量表隨著棧幀的創建而創建,它的大小在編譯時確定,創建時只需分配事先規定的大小即可。在方法運行過程中,局部變量表的大小不會發生改變。
  • Java 虛擬機棧會出現兩種異常:StackOverFlowError 和 OutOfMemoryError
  • StackOverFlowError 若 Java 虛擬機棧的大小不允許動態擴展,那麼當線程請求棧的深度超過當前 Java 虛擬機棧的最大深度時,拋出 StackOverFlowError 異常(出現 StackOverFlowError 時,內存空間可能還有很多
  • OutOfMemoryError 若允許動態擴展,那麼當線程請求棧時內存用完了,無法再動態擴展時,拋出 OutOfMemoryError 異常。
  • Java 虛擬機棧也是線程私有,隨著線程創建而創建,隨著線程的結束而銷毀,數據不是線程共享的,因此不用關心數據一致性問題,也不會存在同步鎖的問題

本地方法棧(C 棧)#

如 Java 使用 c 或者 c++ 編寫的接口服務時,代碼在此區運行

本地方法棧是為 JVM 運行 Native 方法準備的空間,由於很多 Native 方法都是用 C 語言實現的,所以它通常又叫 C 棧。它與 Java 虛擬機棧實現的功能類似,只不過本地方法棧是描述本地方法運行過程的內存模型

#

堆的定義#

堆是用來存放對象的內存空間,幾乎所有的對象都存儲在堆中。堆內存的分為新生代、老年代和永久代。新生代又被進一步分為:Eden 區+Survior1 區+Survior2 區。在 JDK 1.8 中移除整個永久代,取而代之的是一個叫 ** 元空間(Metaspace)** 的區域(永久代使用的是 JVM 的堆內存空間,而元空間使用的是物理內存,直接受到本機的物理內存限制

堆的特點#

  • 線程共享,整個 Java 虛擬機只有一個堆,所有的線程都訪問同一個堆。而程序計數器、Java 虛擬機棧、本地方法棧都是一個線程對應一個。
  • 在虛擬機啟動時創建。
  • 是垃圾回收的主要場所。

不同的區域存放不同生命周期的對象,這樣可以根據不同的區域使用不同的垃圾回收算法,更具有針對性。

堆的大小既可以固定也可以擴展,但對於主流的虛擬機,堆的大小是可擴展的,因此當線程請求分配內存,但堆已滿,且內存已無法再擴展時,就會拋出 OutOfMemoryError 異常。

Java 堆所使用的內存不需要保證是連續的。而由於堆是被所有線程共享的,所以對它的訪問需要注意同步問題,方法和對應的屬性都需要保證一致性。

方法區#

方法區的定義#

Java 虛擬機規範中定義方法區是堆的一個邏輯部分。方法區存放以下信息:

  • 已經被虛擬機加載的類信息
  • 常量
  • 靜態變量
  • 即時編譯器編譯後的代碼

方法區的特點#

  • 線程共享: 方法區是堆的一個邏輯部分,因此和堆一樣,都是線程共享的。整個虛擬機中只有一個方法區。
  • 永久代: 方法區中的信息一般需要長期存在,而且它又是堆的邏輯分區,因此用堆的劃分方法,把方法區稱為 “永久代”。
  • 內存回收效率低。 方法區中的信息一般需要長期存在,回收一遍之後可能只有少量信息無效。主要回收目標是:對常量池的回收;對類型的卸載。
  • Java 虛擬機規範對方法區的要求比較寬鬆。 和堆一樣,允許固定大小,也允許動態擴展,還允許不實現垃圾回收。

運行時常量池#

方法區中存放:類信息、常量、靜態變量、即時編譯器編譯後的代碼。常量就存放在運行時常量池中。

當類被 Java 虛擬機加載後, .class 文件中的常量就存放在方法區的運行時常量池中。而且在運行期間,可以向常量池中添加新的常量。如 String 類的 intern () 方法就能在運行期間向常量池中添加字符串常量

JVM 控制參數#

image.png
如圖所示,常用的一些控制參數如下:

  • -Xms設置堆的最小空間大小。

  • -Xmx設置堆的最大空間大小。

  • -XX:NewSize設置新生代最小空間大小。

  • -XX:MaxNewSize設置新生代最大空間大小。

  • -XX:PermSize設置永久代最小空間大小。

  • -XX:MaxPermSize設置永久代最大空間大小。

  • -Xss設置每個線程的堆棧大小

沒有直接設置老年代的參數,但是可以設置堆空間大小和新生代空間大小兩個參數來間接控制。 老年代空間大小 = 堆空間大小 - 年輕代大空間大小

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。