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而言!