Java虛擬機學習之Java內存區域篇

一、運行時數據區域


(圖片來自網絡)
上圖是Java運行時內存區域模型,這些區域都有各自的用途,以及創建和銷燬的時間,有的區域隨着虛擬機進程的啓動而存在,有些區域則依賴用戶線程的啓動和結束而建立和銷燬。

二、程序計數器

程序計數器是線程私有的,每個線程獨立存儲,是所執行的字節碼的行號指示器。字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令。
如果線程正在執行一個Java方法,計數器記錄的是正在執行的虛擬機字節碼指令的地址。

如果線程正在執行一個Native方法,計數器值爲空(Undefined)。

三、Java虛擬機棧

Java虛擬機棧也是線程私有的,它的生命週期與線程相同。
是描述Java方法的內存模型。
每個方法在執行的同時都會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈接、方法出口等。
每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。
Java虛擬機規範中,對這個區域規定了兩種異常狀況:
1、如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError。
2、如果虛擬機棧可以動態擴展。如果擴展時無法申請到足夠的內存,會拋出OutOfMemoryError。

四、本地方法棧

本地方法棧與Java虛擬機棧作用相似。區別就是本地方法棧爲虛擬機使用的Native方法服務。
也會拋出StackOverflowError和OutOfMemoryError。

五、Java堆

Java堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。
用於存放對象實例,幾乎所有的對象實例(數組)都在這裏分配內存。(但是還有棧上分配技術、標量替換優化技術)
是垃圾收集器管理的主要區域,有時候被稱爲GC堆。
可細分爲:新生代、老年代。
再細緻一點可分爲:Eden空間、From Survivor空間、To Survivor空間。
從內存分配角度看:可能劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer)

六、方法區

是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
Java虛擬機規範把方法區描述爲堆的一個邏輯部分,但是有一個別名叫非堆(Non-Heap)。
也可以稱爲“永久代”。
這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載。

運行時常量池是方法區的一部分。用於存放編譯器產生的各種字面量和符號引用。
運行時常量池相對於Class文件常量池的另外一個重要特徵是具備動態性,運行期間也可能將新的常量放入池中,如String類的intern方法。

七、對象的創建

當虛擬機遇到一條new指令時:
1、首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,則執行類加載。
2、類加載檢查通過後,虛擬機將會爲新生對象分配內存,內存的大小在類加載完成後便可完全確定。分配內存要考慮併發。
3、初始化內存空間爲零值(不包括對象頭)。這一步操作保證了對象實例字段在Java代碼中可以不賦值就直接使用。
4、虛擬機對對象進行必要的設置,這個對象是哪個類的實例、如何找到元數據信息、對象的哈希碼、對象的GC分代年齡等,這些信息存放在對象頭中。
5、執行<init>方法,把對象按照程序員的意願進行初始化。

八、對象的內存佈局

三塊區域:對象頭、實例數據、對齊填充。
1、對象頭:
1.1 存儲對象自身的運行時數據。(哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等)
1.2 類型指針,指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
注意:如果對象是數組,那麼在對象頭中還必須有一塊用於記錄數組長度的數據。

2、實例數據
對象真正存儲的有效信息
3、對齊填充
並不是必然存在的。

九、對象的訪問定位

1、句柄訪問,最大的好處是reference中存儲的是文檔的句柄地址,在對象被移動時只改變句柄中的實例數據指針,而reference本身不需要修改。
2、直接指針訪問,速度快,節省了一次指針定位的時間開銷。

(圖片來自網絡)

發佈了206 篇原創文章 · 獲贊 259 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章