Java虛擬機運行時數據區
所有線程共享接口:方法區,堆(Heap);
線程隔離的數據區:虛擬機棧(VM Stack),本地方法棧(Native Stack),程序計數器(Program Counter Register)
1. 程序計數器
唯一一個沒有規定任何OOM情況的區域
當前線程所執行的字節碼的行號指示器。
— 字節碼解釋器工作時通過改變這個計數器的值來選取下一條需要執行的指令(分支/循環/跳轉/異常處理/線程恢復等基礎功能)都需要依賴這個計數器來完成
— 多線程時,為了線程切換後能恢復正確執行位置,每條線程都需要一個獨立的程序計數器,各線程之間計數器互不影響
— 當線程正執行的是一個Java方法,計數器記錄的是正在執行的虛擬機字節碼指令地址
— 正在執行的是Native方法:計數器值為空(undefined)
2. Java虛擬機棧
線程私有的,生命周期與線程相同。
虛擬機棧描述的是Java方法執行的內存模型。
— 每個方法在執行的同時都會創建一個棧幀(Stack Frame)(指向堆的地址)用於存儲局部變量表/操作數棧/動態連接/方法出口等信息
— 每個方法從調用直至執行完成的過程==>一個棧幀在虛擬機棧中入棧到出棧的過程
異常:
— 線程請求的棧深度大於虛擬機所允許的深度:StackOverflowError異常
— 虛擬機棧動態擴展時無法申請到足夠內存:OutOfMemory
與對象內存分配關系最密切的內存區域:堆內存和棧內存
— 這裏的棧是指虛擬機棧中局部變量/虛擬機棧表部分
— 局部變量表存放:
— 編譯期可知的8種基本數據類型
— 對象引用(引用類型)
— return address類型(指向一條字節碼指令的地址)
3. 本地方法棧
虛擬機棧為虛擬機執行java方法(即字節碼)服務而本地方法棧為虛擬機使用到的Native方法服務
異常:同虛擬機棧
4. Java堆
所有線程共享一塊內存區域
在虛擬機啟動時創建,用於存放對象實例。
根據垃圾收集器的分代收集算法,堆還可以分為:新生代,老年代…,劃分的目的時為了更好的回收內存/更快地分配內存。
Java堆可以處理邏輯上連續,物理上不連續的內存空間
— 當前主流虛擬機都是按可擴展實現的(通過-Xmx和-Xms控制,參數一致則不可擴展)
異常:
— 堆中沒有內存完成實例分配,且堆也無法再擴展時OOM異常
5. 方法區
各線程共享內存區域
用於存儲已被虛擬機加載的類信息/常量/靜態變量/即時編譯器編譯後的代碼等數據
方法區不需要連續的內存,可選擇固定大小或可擴展,可選擇不實現垃圾收集(但並不代表進入方法區就永久存在了)
— 方法區的內存回收目標:針對常量池的回收和堆類型的卸載
異常:
— 當方法區無法滿足內存分配需求時OOM異常
6. 運行時常量池
運行時常量池是方法區的一部分。
Class文件中除了有類的版本/字段/方法/接口等描述信息外,還有常量池(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放
7. 直接內存
不是虛擬機運行時數據區的一部分,也與Java虛擬機規範中定義的內存區域無關
基於通道(Channel)與緩沖區(Buffer)的I/O方式
它可以使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復制數據
Java虛擬機運行時數據區