五、JVM 系列(1) —— 記憶體區域
1. Java 虛擬機器執行時資料區域
Java 虛擬機器在執行 Java 程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。

Java虛擬機器執行時資料區.png
2. 各區域解釋
詳細模型圖:

Java虛擬機器記憶體模型.png
2.1 程式計數器
說明:
-
程式計數器是一塊較小的記憶體空間,它可以看作當前執行緒所執行的 位元組碼的行號指示器 。
-
由於 Java 虛擬機器的 多執行緒 是通過 執行緒 輪流切換 並 分配處理器執行時間 的方式來實現的。在任何一個確定的時刻,一個處理器(對於多核處理器來說是一個核心)都只會執行一條執行緒中的指令。因此,為了執行緒切換後能恢復到正確的執行位置,每條執行緒都需要有一個獨立的程式計數器,各條執行緒之間計數器互不影響,獨立儲存,我們稱這類記憶體區域為 “執行緒私有” 的記憶體。
-
如果執行緒執行的是一個 Java 方法 ,這個計數器記錄的是正在執行的 虛擬機器位元組碼指令的 地址。
-
如果執行緒執行的是一個 Native 方法 ,這個計數器值則為 空(Undefined)
異常:
- 此記憶體區域是唯一 一個在 Java 虛擬機器規範中 沒有規定任何 OutOfMemoryError 情況的區域。
2.2 虛擬機器棧
說明:
-
虛擬機器棧 (Java Virtual Machine Stacks) 的生命週期與執行緒相同。
-
虛擬機器棧描述的是 Java 方法執行的記憶體模型:每個方法在執行的同時都會建立一個 棧幀 (Stack Frame) 。
-
棧幀:是方法執行時的基礎資料結構。棧幀包括
-
區域性變量表 : 所需的記憶體空間在編譯期間完成分配,存放了編譯器可知的 各種基本資料型別 、 物件引用 和 returnAddress型別
-
運算元棧
-
動態連結
-
方法返回地址
-
異常:
-
StackOverflowError:當執行緒請求的 棧深度 大於虛擬機器所允許的深度。
-
OutOfMemoryError:當前大部分的 Java 虛擬機器都可以動態擴充套件,如果擴充套件時無法申請到足夠的記憶體,就會丟擲該異常。
2.3 本地方法棧
說明:
-
與虛擬機器棧所發揮的作用是非常相似的
-
虛擬機器棧為虛擬機器執行 Java 方法 服務
-
本地方法棧為虛擬機器執行 Native 方法 服務
異常:
與虛擬機器棧一樣
-
StackOverflowError
-
OutOfMemoryError
2.4 堆
說明:
-
在虛擬機器啟動時 建立 。
-
此記憶體區域的唯一目的就是 存放 物件例項 ,幾乎所有的物件例項都在這裡分配記憶體。 但是也有例外,隨著JIT編譯器的發展與逃逸技術逐漸成熟, 棧上分配、標量替換 優化技術會導致部分物件例項不分配在堆上,而是棧記憶體。
-
Java 堆是垃圾收集器管理的 主要區域,因此很多時候也被叫做 GC堆。
-
從 記憶體回收 角度:
- Java 堆可以細分為: 新生代和老年代 ;這樣劃分的目的是為了 更好的回收記憶體。
-
從 記憶體分配 角度:
- 執行緒共享的 Java堆 中可能劃分出多個執行緒私有的 分配緩衝區(Thread Local Allocation Buffer, TLAB) ;這樣劃分的目的是為了 更好的分配記憶體。
異常:
- OutOfMemoryError :如果在堆中沒有 足夠的記憶體 完成 例項分配,並且 堆 也無法再擴充套件時,將會丟擲 OutOfMemoryError異常。
2.5 方法區(永久代)
說明:
-
用於儲存已被虛擬機器載入的 類資訊 、 常量 、 靜態變數 、 即時編譯器編譯後的程式碼 等資料。
-
Java虛擬機器規範對方法區的限制非常寬鬆,方法區可以選擇不實現垃圾收集。垃圾收集行為在這一區域是比較少出現的。
-
該區域記憶體回收目標是針對 常量池的回收 和 對型別的解除安裝 。
異常:
- OutOfMemoryError: 根據Java虛擬機器規範的規定,當方法區無法滿足記憶體分配需求時,將丟擲 OutOfMemoryError 異常。
2.6 執行時常量池
說明:
-
執行時常量池(Runtime Constant Pool) 是方法區的一部分。
-
用於存放編譯器生成的各種 字面量 和 符號引用 。
-
執行時常量池除了編譯期產生的Class檔案的常量池,還可以在執行期間,將新的常量加入常量池,比較常見的是String類的intern()方法。
異常:
- OutOfMemoryError: 既然是方法區的一部分,自然受到方法區記憶體的限制,當常量池無法再申請到記憶體時將丟擲 OutOfMemoryError 異常。