目錄
- 1.1主力機型
- 1. HotSpot VM
- 1.2體系結構
- 1.3 運行時資料區
- 1.3.1 程式計數器
- 1.3.2 虛擬機堆疊
- 1.3.3 本地方法堆疊
- 1.3.4 堆
- 1.3.5 方法區
- 1.4 物件如何存放
- 1.4.1 物件的創建程序
- 1.4.2 物件的記憶體布局
- 1.物件頭(HotSpot虛擬機物件的物件頭部分包括兩類資訊)
- 2. 實體資料
- 3. 對齊填充
- 1.4.3 物件的訪問定位
1.1主力機型
1. HotSpot VM
HotSpot VM是OracleJDK和OpenJDK中的默認Java虛擬機,也是目前使用范圍最廣的Java虛擬機,
在JDK8的時候,移除了永久代,
1.2體系結構
- 每個JVM都有一個類加載子系統,它根據給定的全限定名來載入類(或介面),
- 每個JVM都有一個執行引擎,它負責執行那些包含在被載入類的方法中的指令,
- 當JVM執行一個程式時,它需要記憶體來存盤很多東西,例如:位元組碼、從已載入的class的檔案中得到的其他資訊、程式創建的物件、傳遞給方法的引數,回傳值、區域變數,以及運算中間結果等等,JVM把這些東西都組織到幾個“運行時資料區”中,以便管理,

1.3 運行時資料區
1.3.1 程式計數器
程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼行號指示器,(可以理解為位元組碼執行到哪一行了,底層存盤的是偏移量),在Java虛擬機的概念模型里,位元組碼解釋器作業時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,它是程式控制流的指示器,分支、回圈、跳轉、例外處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成,并且每一個執行緒都需要有一個獨立的程式計數器,各執行緒之間計數器互不影響,我們稱這類記憶體區域為“執行緒私有”的記憶體,
1.3.2 虛擬機堆疊
虛擬機堆疊也是執行緒私有的,它的生命周期與執行緒相同,每一個方法被呼叫直至執行完畢的程序,就對應著一個堆疊幀在虛擬機堆疊中從入堆疊到出堆疊的程序,
1.3.3 本地方法堆疊
本地方法堆疊與虛擬機堆疊所發揮的作用是非常相似的,其區別只是虛擬機堆疊為虛擬機執行Java方法(也就是位元組碼)服務,而本地方法堆疊則是為虛擬機使用到的本地方法服務,
1.3.4 堆
堆是虛擬機所管理的記憶體中最大的一塊,Java堆是被所有執行緒共享的一塊記憶體區域,在虛擬機啟動時創建,此記憶體區域的唯一目的就是存放物件實體,Java世界里“幾乎”所有的物件實體都在這里分配記憶體,“GC堆”,
1.3.5 方法區
方法區與堆一樣,是各個執行緒共享的記憶體區域,用于存盤已經被虛擬機加載的型別資訊、常量、靜態變數、即時編譯器編譯后的代碼快取等資料,
1.4 物件如何存放
1.4.1 物件的創建程序
(當JVM遇到一條new 指令時將按照如下流程來創建物件)
- 檢查這個指令的引數是否能在常量池中定位到一個類的符號參考(常量池中是否有這個類),并檢查這個符號參考所代表的類是否已被加載、決議和初始化過,如果沒有,那必須先執行相應的類加載程序,
- 在類加載檢查通過后,接下來虛擬機將為新生物件分配記憶體,物件所需記憶體的大小在類加載完成后便可完全確定,為物件分配空間的任務實際上便等同于把一塊確定大小的記憶體塊從堆中劃分出來,
- 記憶體分配完成之后,虛擬機必須將分配到的記憶體空間(不包括物件頭)都初始化為零值,這步操作保證了物件的實體欄位在Java代碼中可以不賦初始值就直接使用,使程式能訪問到這些欄位的零值,(可以理解為默認值 int i; i 的默認值為0)
- 接下來,Java虛擬機還要對物件進行必要的設罝,例如這個物件是哪個類的實體、如何才能找到類的元資料資訊、物件的哈希碼、物件的GC分代年齡等資訊,這些資訊存放在物件的物件頭之中,
- 到目前為止,建構式,即Class檔案中的init方法(構造方法)還沒有執行,所有的欄位都為默認的零值,物件需耍的其他資源和狀態資訊也還沒有按照預定的意圖構造好,所以,new指令之后會接著執行方法,按照程式員的意愿對物件進行初始化,這樣一個真正可用的物件才算完全被構造出來,
總結:
- 檢查常量池中是否有這個類,如果沒有,那必須先執行相應的類加載程序,
- 在類加載檢查通過后,虛擬機將為新生物件分配記憶體,
- 記憶體分配完成之后,虛擬機將分配到的記憶體空間(不包括物件頭)都初始化為零值,
- Java虛擬機還要對物件進行必要的設罝,例如這個物件是哪個類的實體、如何才能找到類的元資料資訊、物件的哈希碼、物件的GC分代年齡等資訊,這些資訊存放在物件的物件頭之中,
- new指令執行init方法,按照程式員的意愿對物件進行初始化,這樣一個真正可用的物件才算完全被構造出來,
1.4.2 物件的記憶體布局
在HotSpot虛擬機里,物件在堆記憶體中的存盤布局可以劃分為三個部分:物件頭、實體資料、對齊填充,
1.物件頭(HotSpot虛擬機物件的物件頭部分包括兩類資訊)
- 第一類是用于存盤物件自身的運行時資料,如哈希碼、00分代年齡、鎖狀態標志、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等,這部分資料的長度在32位和64位的虛擬機中分別為32個位元和64個位元,官 方稱它為“Mark Word”,考慮到虛擬機的空間效率,Mark Word被設計成一個有著動態定義的資料結構, 以便在極小的空間記憶體儲盡量多的資料,根據物件的狀態復用自己的存盤空間,
- 第二類是型別指標,即物件指向它的型別元資料的指標,虛擬機通過這個指標來確定該物件是哪 個類的實體,并不是所有的虛擬機實作都必須在物件資料上保留型別指標,換句話說,查找物件的元資料 資訊并不一定要經過物件本身,
- 此外,如果物件是一個Java陣列,那在物件頭中還必須有一塊用于記錄陣列長度的資料,因為虛擬機 可以通過普通Java物件的元資料資訊確定物件的大小,但是如果陣列的長度是不確定的,將無法通過元資料中的資訊推斷出陣列的大小,

2. 實體資料
實體資料部分是物件真正存盤的有效資訊,即我們在程式代碼里面所定義的各種型別的欄位內容,無論是從父類繼承下來的,還是在子類中定義的欄位都必須記錄起來,(相同寬度的欄位總是分配到一起存放),
3. 對齊填充
僅僅起著占位符的作用,由于HotSpot虛擬機的自動記憶體管理要求物件起始地址必須是8位元組的整數倍,換句話說就是任何物件的大小都必須是8位元組的整數倍,物件頭部分已經被精心設計成正好是8位元組的倍數,而如果物件實體資料部分沒有對齊的話,就需要通過對齊填充來補全,
1.4.3 物件的訪問定位
Java程式會通過堆疊上的reference來操作堆上的具體物件,《Java虛擬機規范》里面只規定了reference是一個指向物件的參考,并沒有定義這個參考應該通過什么方式去定位、訪問到堆中物件的具體位置,所以物件的訪問方式也是由虛擬機實作而規定的,
主流的方式有:
- 使用句柄
- 直接指標(HotSpot主要使用這種)
-
使用句柄:如果使用句柄訪問的話,堆中將可能會劃分出一塊記憶體來作為句柄池,reference中存盤的就是物件的句柄地址,而句柄中包含了物件實體資料與型別資料各自具休的地址資訊,使用句柄來訪問的最大好處就是reference中存盤的是穩定句柄地址,在物件被移動(垃圾收集時移動物件是非常普遍的行為)時只會改變句梅中的實體資料指鏟,而reference本身不需要被修改,

-
直接指標:如果使用直接指標訪問的話,reference中存盤的直接就是物件地址,如果只是訪問物件本身的話,就不需要多一次間接訪問的開銷,使用直接指標來訪問最大的好處就是速度更快,它節省了一次指標定位的時間開銷,由于物件汸問在reference中非常頻繁,因此這類開銷積少成多也是一項極為可觀的執行成本,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/386764.html
標籤:其他
