相關文章
Java虛擬機系列算法
在前一篇文章中咱們學習了Java虛擬機的結構原理與運行時數據區域,那麼咱們大概知道了Java虛擬機的內存的概況,那麼內存中的數據是如何建立和訪問的呢?這篇文章會給你答案。數組
對象的建立一般是經過new一個對象而已,當虛擬機接收到一個new指令時,它會作以下的操做。
(1)判斷對象對應的類是否加載、連接、初始化
虛擬機接收到一條new指令時,首先會去檢查這個指定的參數是否能在常量池中定位到一個類的符號引用,而且檢查這個符號引用表明的類是否已被類加載器加載、連接和初始化過。若是沒有則先執行相應的類加載過程。關於類加載器咱們在前一篇文章中已經提到過,這裏再也不贅述。安全
(2)爲對象分配內存
類加載完成後,接着會在Java堆中劃分一塊內存分配給對象。內存分配根據Java堆是否規整,有兩種方式:微信
Java堆的內存是否規整根據所採用的來及收集器是否帶有壓縮整理功能有關,關於垃圾收集器,本系列後面的文章會介紹。併發
(3)處理併發安全問題
建立對象是一個很是頻繁的操做,因此須要解決併發的問題,有兩種方式:app
(4)初始化分配到的內存空間
將分配到的內存,除了對象頭都初始化爲零值。oop
(5)設置對象的對象頭
將對象的所屬類、對象的HashCode和對象的GC分代年齡等數據存儲在對象的對象頭中。源碼分析
(6)執行init方法進行初始化
執行init方法,初始化對象的成員變量、調用類的構造方法,這樣一個對象就被建立了出來。佈局
對象建立完畢,而且已經在Java堆中分配了內存,那麼對象在堆內存是如何進行佈局的呢?
以HotSpot虛擬機爲例,對象在堆內存的佈局分爲三個區域,分別是對象頭(Header)、實例數據(Instance Data)、對齊填充(Padding)。學習
對象的內存佈局以下圖所示。
HotSpot中採用了OOP-Klass模型,它是用來描述Java對象實例的一種模型,OOP(Ordinary Object Pointer)指的是普通對象指針,而Klass用來描述對象實例的具體類型。
HotSpot中,用instanceOopDesc 和 arrayOopDesc 來描述對象頭,其中arrayOopDesc對象用於描述數組類型。
instanceOopDesc的代碼以下所示。
openjdk/hotspot/src/share/vm/oops/instanceOop.hpp
class instanceOopDesc : public oopDesc {
public:
// aligned header size.
static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
// If compressed, the offset of the fields of the instance may not be aligned.
static int base_offset_in_bytes() {
// offset computation code breaks if UseCompressedClassPointers
// only is true
return (UseCompressedOops && UseCompressedClassPointers) ?
klass_gap_offset_in_bytes() :
sizeof(instanceOopDesc);
}
static bool contains_field_offset(int offset, int nonstatic_field_size) {
int base_in_bytes = base_offset_in_bytes();
return (offset >= base_in_bytes &&
(offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
}
};複製代碼
能夠看出instanceOopDesc繼承自oopDesc:
openjdk/hotspot/src/share/vm/oops/oop.hpp
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
Klass* _klass;
narrowKlass _compressed_klass;
} _metadata;
// Fast access to barrier set. Must be initialized.
static BarrierSet* _bs;
...
}複製代碼
oopDesc中包含兩個數據成員:_mark 和 _metadata。其中markOop類型的_mark對象指的是前面講到的Mark World。_metadata是一個共用體,其中_klass是普通指針,_compressed_klass是壓縮類指針,它們就是前面講到的元數據指針,這兩個指針都指向instanceKlass對象,它用來描述對象的具體類型。
instanceKlass的代碼以下所示。
openjdk/hotspot/src/share/vm/oops/instanceKlass.hpp
class InstanceKlass: public Klass {
...
enum ClassState {
allocated, // allocated (but not yet linked)
loaded, // loaded and inserted in class hierarchy (but not linked yet)
linked, // successfully linked/verified (but not initialized yet)
being_initialized, // currently running class initializer
fully_initialized, // initialized (successfull final state)
initialization_error // error happened during initialization
};
...
}複製代碼
instanceKlass繼承自Klass ,枚舉ClassState 用來標識對象的加載進度。
知道了OOP-Klass模型,咱們就能夠分析Java虛擬機是如何經過棧幀中的對象引用找到對應的對象實例,以下圖所示。
從圖中能夠看出,經過棧幀中的對象引用找到Java堆中的instanceOopDesc對象,再經過instanceOopDesc中的元數據指針來找到方法區中的instanceKlass,從而肯定該對象的具體類型。
參考資料
《深刻理解Java虛擬機》
《JAVA虛擬機精講》
深刻探究 JVM | klass-oop 對象模型研究
JVM源碼分析之Java對象的建立過程
JVM源碼分析之Java類的加載過程
歡迎關注個人微信公衆號,第一時間得到博客更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。