深入理解JVM之內存區域
以下是閱讀《深入理解Java虛擬機--JVM高級特性與最佳實踐》的讀書筆記
本文結構:
運行時數據區域
HotSpot虛擬機對象那些事兒
對象的創建
對象的內存布局
對象的訪問定位
運行時數據區域
程序計數器---當前線程所執行的字節碼的行號指示器。
虛擬機棧---描述的是Java方法執行的內存模型,每個方法在執行的同時會創建一個棧幀。
局部變量表---存放編譯期可知的各種基本數據類型、對象引用,其所需的內存空間在編譯期間完成分配。
本地方法棧---與虛擬機棧類似,但虛擬機棧為虛擬機執行Java方法服務,而本地方法棧為虛擬機使用到的Native方法服務。
Java堆---存儲對象實例,因是垃圾收集器管理的主要區域,也稱為GC堆。
方法區---存儲虛擬機加載過的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,且這區域的內存回收目標主要是針對常量池的回收和對類型的卸載。
運行時常量池---存放編譯期生成的各種字面量和符號引用,具備動態性,運行期也可能將新的常量放入池中(String的intern方法)
直接內存---NIO類使用Native函數庫直接分配堆外內存,然後通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。
HotSpot虛擬機對象那些事兒
對象的創建
① 虛擬機遇到new指令,先去檢查這個指令的參數在常量池中能不能定位到那個類的符號引用,並檢查這個符號引用代表的類是否被加載、解析和初始化過,若無,必須先執行類加載過程。
② 類加載檢查通過後,虛擬機為新生對象分配內存。
a) 指針碰撞---假設Java堆中的內存是絕對規整的,中間放一個指針作為分界點的指示器,分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離。
b) 空閑列表---假設Java堆中的內存不是規整的,那虛擬機需要維護一個列表,記錄哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,並更新列表上的記錄。
c) 選用哪種分配方式由Java堆內存塊是否規整決定,Java堆是否規整又由所采用的垃圾收集器是否帶有壓縮管理功能決定。
d) 對象創建很頻繁的情況下會導致虛擬機在分配內存的時候出現指針不同步的現象
1) 對分配內存空間的動作進行同步處理---CAS配上失敗重試方式保證原子性。
2) 把內存分配的動作按照線程劃分在不同的空間之中進行---哪個線程要分配內存,就在哪個線程的TLAB上分配,只有TLAB用完並分配新的TLAB時,才需要同步鎖定。
e) 虛擬機需要將分配到的內存空間都初始化為零值(不包括對象頭)
f) 虛擬機要對對象進行必要的設置,將一些信息裝進對象頭中。
g) 只要在執行new指令之後接著執行<init>方法,這個對象才算是真正創建。
對象的內存布局
① 對象頭
用於存儲對象自身的運行時數據
類型指針(虛擬機通過這個指針來確定這個對象是哪個類的實例)
(如果對象是一個Java數組的話,還需要多一塊用於記錄數組長度的數據)
② 實例數據(對象真正存儲的有效信息)
③ 對齊填充(占位符的作用,因為對象的大小必須是8字節的整數倍)
對象的訪問定位
① 句柄---Java堆中將會劃分出一塊內存來作為句柄池,Java棧本地變量表中的reference存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。
② 直接指針---Java堆對象的布局中就必須要包含對象實例數據,Java棧本地變量表中的reference存儲的就是這個對象地址。
深入理解JVM之內存區域