[JVM] Java 記憶體區域與記憶體溢位異常
[JVM] Java 記憶體區域與記憶體溢位異常
@TOC
手機使用者請
橫屏
獲取最佳閱讀體驗,REFERENCES
中是本文參考的連結,如需要連結和更多資源,可以關注其他部落格釋出地址。
平臺 | 地址 |
---|---|
CSDN | blog.csdn.net/sinat_28690… |
簡書 | www.jianshu.com/u/3032cc862… |
個人部落格 | yiyuery.github.io/NoteBooks/ |
筆者將從概念上介紹 Java 虛擬機器器記憶體的各個區域,講解這些區域的作用、服務物件以及其中可能產生的問題
執行時資料區域
執行時資料區域主要有:
- 方法區 Method Area
- 堆 Java Heap
- 虛擬機器器棧
- 本地方法棧
- 程式計數器
其中方法區和堆由所有執行緒共享,其餘為執行緒內部隔離的資料區域。
程式計數器
一塊較小的記憶體區域,可以看做是當前執行緒所執行的位元組碼的行號指示器。
由於多執行緒的切換時通過執行緒輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(核心)都只會執行一條執行緒中的指令。為了執行緒切換後能恢復到正確的執行位置,各執行緒間必須各自擁有一個獨立的程式計數器,即執行緒私有
記憶體。
另外,執行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高階特性與最佳實踐》讀書筆記
更多
掃碼關注
架構探險之道
,回覆文章標題,獲取本文相關原始碼和資源連結
知識星球(掃碼加入獲取歷史原始碼和文章資源連結)