JVM 記憶體區域

JiangYue發表於2018-04-26

JVM 在執行 Java 程式的過程中會把它所管理的記憶體劃分為如下幾個資料區域:

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

程式計數器

程式計數器是一塊很小的記憶體空間,是當前執行緒所執行位元組碼的行號指示器,執行緒間私有不共享。執行 Java 方法時計數器的值為位元組碼指令的地址,執行 Native 方法時值為空。不會出現OutOfMemoryError。

虛擬機器棧

虛擬機器棧為執行緒私有,生命週期與執行緒相同,描述 Java 方法執行的記憶體模型。方法執行時建立棧幀,用於儲存區域性變數表、運算元棧、方法出口等資訊。

區域性變數表存放了編譯器可知的基本資料型別、物件引用和 returnAddress型別(位元組碼指令地址),所需記憶體空間在編譯器完成分配,執行期間不改變大小。

執行緒請求棧深度大於虛擬機器允許深度時丟擲 StackOverflowError(大部分虛擬機器可以動態擴容,但也允許固定長度的虛擬機器棧),擴充套件時無法申請到足夠記憶體丟擲 OutOfMemoryError。

本地方法棧

本地方法棧為虛擬機器執行 Native 方法服務,執行緒隔離。虛擬機器規範中無強制規定,Sun HostSpot 將本地方法棧和虛擬機器棧合二為一。能夠丟擲 StackOverflowError 和 OutOfMemoryError 異常。

Java 堆

Java 堆是 JVM 所管理記憶體中最大的一塊,所有執行緒共享,在虛擬機器啟動時建立,用於存放物件例項,是垃圾回收的主要區域。

從記憶體回收的角度可分為新生代和老年代,再細緻劃分可分為 Eden 空間、From Survivor 空間 和 To Survivor 空間等。

Java 堆可以處於物理上不連續的記憶體空間內,只要邏輯上連續即可。

在堆中沒有記憶體完成例項分配也無法再擴充套件時丟擲 OutOfMemoryError。

方法區

方法區由各個執行緒共享,用於儲存已被 JVM 載入的類資訊、常量、靜態變數、即使編譯器編譯後的程式碼等。

JVM 規範把方法區描述為堆的一個邏輯部分,別名 Non-Heap(目的是與 Java 堆區分開)。

JVM 規範對方法區的限制非常寬鬆,不需要連續記憶體、固定大小或可擴充套件均可,還可以不實現垃圾回收。垃圾收集行為在這個區域較少出現。

方法區包含執行時常量池,Class 檔案中的常量資訊在類載入後存放於執行時常量池中。執行時常量池具備動態性,執行期間也可能將新的常量放入池中。

當方法區無法滿足記憶體分配需求時會丟擲 OutOfMemoryError。

直接記憶體 *

直接記憶體不是 JVM 執行時資料區的一部分,也不是 JVM 規範中定義的記憶體區域,但這部分記憶體會被頻繁使用,而且可能會導致 OutOfMemoryError 異常。

JDK 1.4 中新加入的NIO(New Input / Output)類引入了一種基於通道與緩衝區的 IO 方式,可以使用 Native 函式庫直接分配堆外記憶體,再通過儲存在 Java 堆中的DirectByteBuffer 物件做為這塊記憶體的引用進行操作。

直接記憶體不受 Java 堆大小限制,但會受到本機總記憶體及處理器定址空間限制,動態擴充套件超出限制會丟擲 OutOfMemoryError 異常。

原文地址

相關文章