JVM--Java內存區域
Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若幹個不同的數據區域,如圖:
1.程序計數器
可以看作是當前線程所執行的字節碼的行號指示器,通俗的講就是用來指示執行哪條指令的。為了線程切換後能恢復到正確的執行位置
Java多線程是通過線程輪流切換並分配處理器執行的,為了能夠使得每個線程都在線程切換後能夠恢復在切換之前的程序執行位置,每個線程都需要有自己獨立的程序計數器
所以程序計數器是線程私有的
2.Java虛擬機棧
虛擬機棧描述的Java方法執行的內存模型,每個方法在執行的同時都會創建一個棧幀
棧幀包括:局部變量表、操作數棧、動態鏈接和返回地址
待補充。。。
線程私有
3.本地方法棧
Java虛擬機棧是對應Java方法;本地方法棧對應Native方法
線程私有
4.Java堆
存放對象實例
是垃圾收集器管理的主要區域
線程共享
5.方法區
存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼
運行時常量池--用於存放編譯器生成的各種字面量和符號引用
線程共享
對象創建
虛擬機遇到一條new指定時,首先檢查指令的參數是否能在常量池中定位到一個類的符號引用,並檢查這個符號引用待代表的類是否已被加載、解析和初始化過,
如果沒有,要先執行類加載過程
類加載完後,虛擬機為新生對象分配內存,對象內存的大小在類加載完後便可完全確認
為對象分配內存相當於把Java堆中劃分一塊出來
在並發情況下,劃分內存不是線程安全的,有2種解決方案:
1.對劃分內存操作進行同步處理
2.每個線程在Java堆中預先分配一塊內存--本地線程分配緩存TALB,只有在TALB用完並分配新的TALB時,才需要同步鎖定
內存分配完成後,虛擬機將分配的內存空間都初始化為零值--這一步操作保證了對象實例字段在Java代碼中可以不賦初始值就直接使用
自此,虛擬機就創建完一個新的對象了
簡潔創建過程:類加載-->分配內存-->把內存空間初始化為零值
對象的內存布局
對象在內存中存儲布局分為3塊區域---對象頭、實例數據、對齊填充
對象頭:
1.存儲對象自身的運行時數據---哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等
2.類型指針---對象指向它的類元數據的指針(並非所有JVM)
3.如果對象是數組,對象頭還存儲用於記錄數組長度的數據
實例數據:
程序代碼中所定義的各種類型的字段內容
對齊填充:
占位符,無特別含義
對象的訪問定位
Java程序通過棧上的reference數據來操作堆上的具體對象;reference是一個指向對象的引用。
具體訪問方式有2中---句柄、直接指針
句柄:
Java堆中劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象的實例數據和類型數據各自的具體地址
好處:穩定,在對象被移動時只會改變句柄中的實例數據指針,而reference本身不需要改變
壞處:開銷大,每次都要2次指針定位
直接指針:
Java堆對象的布局中存有訪問類型數據的相關信息,reference中直接存儲對象的地址
好處:速度快,主要用這種
以上都是針對主流虛擬機Sun HotSpot而言!
JVM--Java內存區域