Java虛擬機內存詳解
一、運行時數據區域
Java虛擬機在執行程序的過程會把它所管理的內存分為若干個不同的區域,這些區域都有各自的用途,以及創建和銷毀時間,根據Java虛擬機規范,Java虛擬機所管理的內存分為以下幾個運行時數據區域:
- 程序計數器
- Java虛擬機棧
- 本地方法棧
- Java堆
- 方法區
- 運行時常量池
- 直接內存
1.1 程序計數器
程序計數器是一塊較小的內存空間,它是JVM內部虛擬寄存器,字節碼解釋器通過改變這個計數器的值來選取嚇一條需要執行的字節碼指令。程序計數器是線程私有的。
1.2 Java虛擬機棧
Java虛擬機棧描述的是Java方法執行的內存模型,每個方法在執行的時候,都會創建棧幀用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息,每個方法從調用到執行完的過程,就對應著一個棧幀在虛擬機棧中入棧到出站的過程,Java虛擬機棧同樣也是線程私有的。
- 局部變量表:局部變量表是一組變量存儲空間,它存放了編譯器可知的各種基本數據類型、引用對象和returnAddress類型,局部變量表所需的內部空間在編譯期間完成分配,當進入一個方法時這個方法需要在棧幀中分配多大的局部變量空間是完全確定的。
1.3 本地方法棧
本地方法棧與虛擬機棧非常相似,他們的區別是虛擬機棧為虛擬機執行Java方法服務,本地方法棧為虛擬機使用到native方法服務。
1.4 Java堆
Java堆是虛擬機管理的內存中最大的一塊,Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動的時候創建,此區域唯一的目的是用來存放對象實例
1.5 方法區
方法區和Java堆一樣也是各個線程共享的內存區域,它用于存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
1.6 運行時常量
運行時常量是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外還有一項信息是常量池
,用于存放編譯期生成的各種字面量和符號引用。
1.7 直接內存
在JDK1.4中新加入了NIO類,引入了一種基于通道與緩沖區的I/O方式,它可以使用native庫函數直接分配堆外內存。
二、對象的內存布局
在HotSpot虛擬機中,對象在內存中存儲的布局可以分為3塊區域:
- 對象頭
- 實例數據
- 對齊填充
2.1 對象頭
對象頭包括兩部分信息,第一部分,用于存儲自身對象的運行時數據,如哈希嗎、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳等,對象頭的另一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定對象是哪個類的實例
2.2 實例數據
實例數據部分是對象真正存儲的有效信息
2.3 對齊填充
對齊填充并不必然存在,也沒用特別的含義,它僅僅起著占位符的作用
三、對象的訪問定位
對象創建之后保存在Java堆中,要想使用對象,需要通過棧上的reference數據在操作 。虛擬機規范只是規定了一個指向對象的具體位置,并沒有定義這個引用通過何種方式去定位、訪問堆中的對象的具體位置,所以對象訪問方式也是取決于虛擬機的實現。
目前有兩種主流的訪問方式,使用句柄和直接指針
- 使用句柄:JVM在Java堆中劃分一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,句柄中包含了對象實例數據和類型數據各自的具體地址信息。
- 直接指針:在reference堆棧中存放的是對象在Java堆中的地址,在堆中還需要存放
對象類型數據
的地址。
這2中方式各有優勢,使用句柄來訪問最大的好處是存儲的是穩定的句柄地址,在對象移動時只會改變句柄池
中的實例數據指針,而reference本身不需要修;使用指針訪問最大的好處是速度更快,它節省了一次指針定位的時間開銷
對象類型數據和對象實例數據的理解:
- 對象實例數據指的是某個類new出來的具體實例
- 對象類型數據指的是Class類信息,定義了一個類的元數據、它包含的成員等