天天看點

[JVM] Java 記憶體區域與記憶體溢出異常

[JVM] Java 記憶體區域與記憶體溢出異常

目錄

  • ​​[JVM] Java 記憶體區域與記憶體溢出異常​​
  • ​​運作時資料區域​​
  • ​​程式計數器​​
  • ​​Java 虛拟機棧​​
  • ​​本地方法棧​​
  • ​​Java 堆​​
  • ​​方法區​​
  • ​​運作時常量池​​
  • ​​直接記憶體​​
  • ​​REFERENCES​​
  • ​​更多​​
手機使用者請​

​橫屏​

​​擷取最佳閱讀體驗,​

​REFERENCES​

​中是本文參考的連結,如需要連結和更多資源,可以關注其他部落格釋出位址。
平台 位址
簡書 ​​https://www.jianshu.com/u/3032cc862300​​
個人部落格 ​​https://yiyuery.github.io/NoteBooks/​​
筆者将從概念上介紹 Java 虛拟機記憶體的各個區域,講解這些區域的作用、服務對象以及其中可能産生的問題

運作時資料區域

運作時資料區域主要有:
  • 方法區 Method Area
  • 堆 Java Heap
  • 虛拟機棧
  • 本地方法棧
  • 程式計數器

其中方法區和堆由所有線程共享,其餘為線程内部隔離的資料區域。

[JVM] Java 記憶體區域與記憶體溢出異常

程式計數器

一塊較小的記憶體區域,可以看做是目前線程所執行的位元組碼的行号訓示器。

由于多線程的切換時通過線程輪流切換并配置設定處理器執行時間的方式來實作的,在任何一個确定的時刻,一個處理器(核心)都隻會執行一條線程中的指令。為了線程切換後能恢複到正确的執行位置,各線程間必須各自擁有一個獨立的程式計數器,即​

​線程私有​

​記憶體。

另外,執行Java方法的時候,計數器記錄的是正在執行的虛拟機位元組碼指令的位址;而執行的是Native方法,記錄的值為空。此記憶體區域是Java虛拟機規範中唯一一個沒有規定任何​

​OutOfMemoryError​

​情況的區域。

Java 虛拟機棧

虛拟機棧描述的是Java方法執行的記憶體模型。和程式計數器一樣,也是​

​線程私有​

​​的。每個方法在執行的同時會建立一個​

​棧幀​

​。

​棧幀​

​用于存儲:局部變量表、操作數棧、動态連結清單、方法出口燈資訊。每個方法從調用直至執行完成的過程,就對應一個棧幀在虛拟機棧中入棧到出棧的過程。

​局部變量表​

​用于存放編譯期可知的各種基本資料類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,不等同于對象本身,可能是一個指向對象的起始位址的引用指針,也可能是個指向一個對象的句柄或其他與此對象相關的位置)和returnAddress類型(指向了一條位元組碼指令的位址)

該區域根據Java虛拟機規範中,定義了兩種異常情況:

  • 線程請求的棧深度大于虛拟機勻速的深度,将抛出​

    ​StackOverflowError​

  • 如果虛拟機棧可以動态擴充,但是擴充時無法申請到足夠記憶體,将會抛出​

    ​OutOfMemoryError​

本地方法棧

它的作用與虛拟機棧相似,差別在于,前者為虛拟機棧提供Native方法服務,後者為虛拟機執行Java方法(位元組碼)服務。

該區域和虛拟機棧抛出的異常情況一樣。

Java 堆

Java 堆是Java虛拟機所管理的記憶體中最大的一塊。被所有線程共享的一塊區域,幾乎所有的對象執行個體都在這裡配置設定記憶體。也是垃圾收集器管理的主要區域,是以也被稱為​

​GC堆​

​。

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

從記憶體配置設定的角度來看,線程共享的Java堆中可能劃分出多個線程的私有配置設定緩沖區(Thread Local Allocation Buffer,TLAB)。

根據Java虛拟機規範的規定,Java堆可以在實體上處于不連續的記憶體空間中,隻要邏輯上連續就可以了。在實作時,可以實作為固定大小的,也可以是可擴充的。

一般都是可擴充的,通過參數進行配置:​

​-Xmx​

​​ 和 ​

​-Xms​

如果在堆中沒有記憶體完成執行個體配置設定,并且堆也無法在擴充時,将會抛出​

​OutOfMemoryError​

​。

方法區

方法區和Java堆一樣,是各個線程共享的記憶體區域,用于存儲已被虛拟機棧加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。

方法區在Java虛拟機規範中被描述為一個邏輯部分,但是它有個别名叫做​

​Non-Heap​

​非堆,目的就是和Java堆區分開。

該區域垃圾回收的主要目标是:針對常量池的回收和類型的解除安裝。

根據Java虛拟機規範,當方法區無法滿足記憶體配置設定需求時,将抛出​

​OutOfMemoryError​

​.

運作時常量池

方法區的一部分,Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有一項資訊是常量池。用于存放編譯器生成的各種字面量和符号引用,這部分内容将在類加載後進入方法區的運作時常量池中存放。

既然屬于方法區的一部分,異常抛出和方法區一緻。

直接記憶體

直接記憶體(Direct Memory)并不是虛拟機運作時資料區的一部分,也不是Java虛拟機規範中定義的記憶體區域。但是這部分卻被頻繁使用,也可能會導緻​

​OutOfMemoryError​

​出現。

JDK1.4後引入的NIO,引入了一中基于通道與緩沖區的I/O方式,可以使用Native函數庫直接配置設定堆外記憶體,然後通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊記憶體的引用進行操作。這樣做是為了在一些場景中提高性能,避免Java堆和Native堆中來回複制資料。

但是,該記憶體的配置設定會受到本地記憶體總量的限制,伺服器管理者在配置虛拟機參數時,可以根據實際記憶體設定​

​-Xmx​

​等參數資訊來調整堆的記憶體容量,來控制直接記憶體可以配置設定的最大容量。

REFERENCES

《深入了解Java虛拟機-JVM進階特性與最佳實踐》讀書筆記

更多

掃碼關注​

​架構探險之道​

​,回複文章标題,擷取本文相關源碼和資源連結