Java內存區域——JVM讀書筆記

Java虛擬機運行時數據區

運行時數據區主要包括:方法區、堆、虛擬機棧、本地方法棧、程序計數器。

其中方法區和棧是線程共享的區域,另外三塊區域是每個線程私有的區域。各個數據區的功能簡單說明如下:

程序計數器:當前線程所執行的字節碼的行號指示器。

虛擬機棧:描述Java方法執行的內存模型——每個方法在執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應一個棧幀在虛擬機棧中入棧到出棧的過程。如果棧的深度大於虛擬機所允許的深度,將拋出StackOverflowError異常。如果棧擴展時無法申請到足夠的內存,將拋出OutOfMemoryError異常(下述的本地方法棧類同)。

本地方法棧:與虛擬機棧的作用類似。虛擬機棧爲Java方法服務,本地方法棧爲Native方法服務。

Java堆:在虛擬機啓動的時候創建,作用是存放對象實例。通過-Xmx和-Xms控制堆的大小。如果在堆中沒有內存完成實例分配,並且堆也無法再擴展時,將拋出OutOfMemoryError異常。

方法區:作用是存儲已被虛擬機加載的類信息、常量、靜態變量等(在HotSpot中稱此區域爲Permanent Generation)。




對象的創建

對象的創建包括四個步驟:類加載檢查、爲新生對象分配內存、初始化爲零值、設置對象頭。

一、類加載檢查

虛擬機遇到一條new指令時,收件將檢查該指令的參數是否能在常量池(運行時常量池是方法區的一部分。Class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。)中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。

二、爲新生對象分配內存

對象所需內存大小在類加載完成後便完全確定,則分配內存等同於把一塊確定大小的內存從Java堆中劃分開來。分兩種情況:Java堆中內存是絕對規整的(稱爲指針碰撞,Bump the pointer)、內存不規整(稱爲空閒列表,Free List)。在規整的Java堆中,用過的內存放一邊,空閒的內存放一邊,中間放一個指針作爲分界點。在不規整的Java堆中,虛擬機維護一個列表記錄哪些塊是可用的。選擇那種分配方式由Java堆是否規整決定,而Java堆是否規整由採用的垃圾收集器是否帶有壓縮整理功能決定。如:在使用Serial、ParNew等帶Compact過程的收集器時,採用的分配算法是指針碰撞;而使用CMS基於Mark-Sweep算法的收集器時,採用空閒列表。

三、分配內存過程需要保證線程安全

對分配內存空間的動作進行同步處理;或者是把內存分配的動作按照線程劃分在不同的空間進行(即本地線程分配緩衝Thread Local Allocation Buffer)。

四、對象頭的設置

說明是哪個類的實例、如何找到類的元數據信息、對象的哈希碼、對象的GC年齡。


對象的訪問定位

Java程序通過棧上得reference數據操作堆上得具體對象。有兩種訪問方式:使用句柄和直接指針。

一、使用句柄

reference中存儲的是穩定的句柄地址,在對象實例數據被移動時(垃圾回收時)只會改變句柄中的實例數據指針,而reference本身不需要改變。

二、使用直接指針

速度快,因爲它節省了一次指針定位的時間開銷。


說明:本文的內容參考書籍《深入理解Java虛擬機(第2版)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章