讓咱們思考這樣一個問題:一個Java對象如何在基於c++實現的系統中運行?對象在JVM內部是如何表示的?它在內存中是如何存儲的......java
1.OOP-Klass 二分模型c++
Java是面向對象的語言,面向對象有三個特徵:封裝、繼承和多態。而HotSpot基於C++實現,C++也是面向對象的語言,那這樣的話爲每個Java類生成一個C++類不就OK了嗎?事實並非這樣,對象在JVM內的表示被設計成了一種新的表示方式:OOP-Klass 二分模型:數組
■ OOP:或OOPS,即普通對象指針,用來描述對象實例信息。多線程
■ Klass :Java類的C++對等體,用來描述對象實例的具體類型。函數
爲何要設計這樣的模型呢?緣由:HotSpot JVM的設計者不想讓每個對象中都含有一個vtable(虛函數表)。oop
2.OOP-Klass模塊:佈局
模塊說明以下:spa
OOP線程
上面列出的是整個Oops模塊的體系結構,其中包含多個子模塊,每一個子模塊對應一個類型,每個類型的OOP都表明一個在JVM內部使用的特定對象類型。在Java應用程序運行過程當中,每建立一個Java類對象,在JVM內部也會相應的建立一個OOP對象來表示Java對象。OOPS類的共同基類型爲oopDesc。根據JVM內部使用的對象業務類型,具備多種oopDesc子類,每種類型的OOP都表明一個在JVM內部使用的特定對象類型。設計
這些OOPS類在JVM內部有着不一樣的用途:
例如:instanceOopDesc表示類型實例,arrayOopDesc表示數組。也就是說,當咱們使用new建立一個Java對象實例的時候,JVM會建立一個instanceOopDesc對象來表示這個Java對象;同理,當咱們使用new建立一個Java數組實例的時候,JVM會建立一個arrayOopDes對象來表示這個數組對象。
在JVM內部,經過instanceOopDesc來表示一個Java對象。對象在內存中的佈局能夠分爲連續的兩部分:instanceOopDesc和實例數據。其中,instanceOopDesc或arrayOopDesc又被稱爲對象頭,對象頭包含兩部分信息:Mark Word(_mark)和元數據指針(_metadata)。其中,Mark Word存儲對象運行時記錄信息,入哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線性ID等;元數據指針指向描述類型的Klass對象的指針,Klass對象包含了實例對象所屬類型的元數據,虛擬機運行時將頻繁使用到這個指針定位到位於方法區內的類型信息。
Klass
和OopDesc是其餘Oop類型的父類同樣,Klass是其餘klass類型的父類。如圖:
Klass向JVM提供兩個功能:
■ 語言層面實現Java類(在Klass基類中已實現)
■ 實現Java對象的分發功能(由Klass的子類提供虛函數實現)
HotSpot JVM的設計者把對象一分爲二,分爲klass和oop,其中oop的職能主要在於表示對象的實例數據,因此其中不含任何虛函數;而klass爲了實現虛函數多態,因此提供了虛函數表。_metadata是一個共用體,其中_klass是普通指針,_compressed_klass是壓縮類指針。這兩個指針都指向instanceKlass對象,它用來描述對象的具體類型。
JVM在運行時,須要一種用來標識Java內部類型的機制。在HotSpot中的解決方案是:爲每個已加載的Java類建立一個instanceKlass對象,用來在JVM層表示Java類。
instanceKlass的內部機構:
在JVM中,對象在內存中的基本存在形式就是oop。那麼,對象所屬的類,在JVM中也是一種對象,所以他們實際上也會被組織成一種oop,即klassOop。一樣的,對於klassOop,也有對應的一個klass來描述,就是klassKlass,也是klass的一個子類。klassKlass做爲oop的klass鏈的端點,以下圖:
在這種設計下,JVM對內存的分配和回收,均可以採用同一的方式來管理。oop通常能夠理解爲咱們常說的對象,klassOop可理解爲Java類在JVM中的表示,klassKlass描述瞭如何表示一個類。爲了更形象的表示一個java類在內存中的存儲結構,請看下圖:
總結:
每個Java類,在被JVM加載的時候,JVM會給這個類建立一個instanceklass,保存在方法區,用來在JVM層表示該Java類。當咱們在Java代碼中,使用new建立一個對象時,JVM會建立一個instanceOopDesc對象,此對象包含了兩部分信息:對象頭和元數據。對象頭中有一些運行時數據,其中就包括和多線程相關的鎖信息;元數據維護的是指針,指向的是對象所屬的類的instaceKlass。