欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

HotSpot的Java對(duì)象模型之Oop-Klass模型詳解

 更新時(shí)間:2023年08月30日 10:19:45   作者:碳基生物與硅  
這篇文章主要介紹了HotSpot的Java對(duì)象模型之Oop-Klass模型詳解,在JVM層面,不僅Java類(lèi)是對(duì)象,Java 方法也是對(duì)象, 字節(jié)碼常量池也是對(duì)象,一切皆是對(duì)象,JVM使用不同的oop-klass模型來(lái)表示各種不同的對(duì)象,需要的朋友可以參考下

Java對(duì)象模型

Java中Object類(lèi)是所有對(duì)象的父類(lèi),這是語(yǔ)言層面的定義。

在JVM層面,不僅Java類(lèi)是對(duì)象,Java 方法也是對(duì)象, 字節(jié)碼常量池也是對(duì)象,一切皆是對(duì)象。

JVM使用不同的oop-klass模型來(lái)表示各種不同的對(duì)象。

而在技術(shù)落地時(shí),這些不同的模型就使用不同的 oop 類(lèi)(instanceoop  methodoop constmethodoop等等)和 klass 類(lèi)來(lái)表示 。

由于JVM使用C/C++編寫(xiě),因此這些 oop 和 klass 類(lèi)便是各種不同的C++類(lèi)。

對(duì)于Java類(lèi)型與實(shí)例對(duì)象,只叫使用 instanceOop 和 instanceKlass 這 2 個(gè) C++類(lèi)來(lái)表示。

refrence、oop、klass的關(guān)系如下所示:

Oop繼承體系

oop(ordinary object pointer,普通對(duì)象指針)。是HotSpot用來(lái)表示Java對(duì)象的實(shí)例信息的一個(gè)體系

既在JVM層面,oop用于表示對(duì)象(oop本質(zhì)上是一個(gè)指向內(nèi)存中對(duì)象的起始存儲(chǔ)位置的指針)。

在hotspot/share/oops/oopsHierarchy.hpp 文件中,對(duì)oop的定義如下:

typedef class oopDesc*                    oop;
typedef class   instanceOopDesc*            instanceOop;
typedef class   arrayOopDesc*               arrayOop;
typedef class     objArrayOopDesc*            objArrayOop;
typedef class     typeArrayOopDesc*           typeArrayOop;

其中oop是Oop體系中的最高父類(lèi),整個(gè)繼承體系如下所示,不同的oop用于表示不同的類(lèi)

例如instanceOop表示Java中普通的對(duì)象,arrayOop則表示數(shù)組對(duì)象。

oop(對(duì)象)由對(duì)象頭,對(duì)象體(實(shí)例數(shù)據(jù)),對(duì)齊填充三部分組成。

對(duì)象頭

對(duì)象頭中存儲(chǔ)了對(duì)象很多java內(nèi)部的信息,如hash碼,對(duì)象所屬的年代,對(duì)象鎖,鎖狀態(tài)標(biāo)志,偏向鎖(線程)ID,偏向時(shí)間等

Java對(duì)象頭一般占有2個(gè)機(jī)器碼(機(jī)器一次處理的數(shù)據(jù)位數(shù))。

如果對(duì)象是數(shù)組類(lèi)型,則需要3個(gè)機(jī)器碼,因?yàn)镴VM虛擬機(jī)可以通過(guò)Java對(duì)象的元數(shù)據(jù)信息確定Java對(duì)象的大小,但是無(wú)法從數(shù)組的元數(shù)據(jù)來(lái)確認(rèn)數(shù)組的大小,所以用一塊區(qū)域用來(lái)記錄數(shù)組長(zhǎng)度。

HotSpot虛擬機(jī)的對(duì)象頭包括兩部分信息

第一部分為 Mark Word

第二部分為 class pointer

如果是數(shù)組對(duì)象,那么還有數(shù)組長(zhǎng)度。 

普通對(duì)象和數(shù)組對(duì)象的對(duì)象頭結(jié)構(gòu)如下(32位為例):

Mark Word

這部分主要用來(lái)存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如hashcode、gc分代年齡等。

mark word 的位長(zhǎng)度為JVM的一個(gè)機(jī)器碼的大小,為了存儲(chǔ)更多的信息,JVM將機(jī)器碼的最低兩個(gè)位設(shè)置為標(biāo)記位,不同標(biāo)記位下的Mark Word示意如下:

其中各部分的含義如下:

  • lock: 2位的鎖狀態(tài)標(biāo)記位,由于希望用盡可能少的二進(jìn)制位表示盡可能多的信息,所以設(shè)置了lock標(biāo)記。該標(biāo)記的值不同,整個(gè)mark word表示的含義不同。
  • biased_lock : lock 狀態(tài)(2位。和是否偏向鎖共同作用,示意如下)
    • 0 01 無(wú)鎖
    • 1 01 偏向鎖
    • 0 00 輕量級(jí)鎖
    • 0 10 重量級(jí)鎖
    • 0 11 GC標(biāo)記
  • biased_lock:對(duì)象是否啟用偏向鎖標(biāo)記,只占1個(gè)二進(jìn)制位。為1時(shí)表示對(duì)象啟用偏向鎖,為0時(shí)表示對(duì)象沒(méi)有偏向鎖。
  • age:4位的Java對(duì)象年齡。在GC中,如果對(duì)象在Survivor區(qū)復(fù)制一次,年齡增加1。當(dāng)對(duì)象達(dá)到設(shè)定的閾值時(shí),將會(huì)晉升到老年代。默認(rèn)情況下,并行GC的年齡閾值為15,并發(fā)GC的年齡閾值為6。由于age只有4位,所以最大值為15,這就是-XX:MaxTenuringThreshold選項(xiàng)最大值為15的原因。
  • identity_hashcode:25位的對(duì)象標(biāo)識(shí)Hash碼,采用延遲加載技術(shù)。調(diào)用方法System.identityHashCode()計(jì)算,并會(huì)將結(jié)果寫(xiě)到該對(duì)象頭中。當(dāng)對(duì)象被鎖定時(shí),該值會(huì)移動(dòng)到管程Monitor中。
  • thread:持有偏向鎖的線程ID。
  • epoch:偏向時(shí)間戳。
  • ptr_to_lock_record:指向棧中鎖記錄的指針。
  • ptr_to_heavyweight_monitor:指向管程Monitor的指針。

64位與32位組成相同,主要是存儲(chǔ)位數(shù)長(zhǎng)度不同:

Klass Pointer

這一部分用于存儲(chǔ)對(duì)象的類(lèi)型指針,該指針指向它的類(lèi)元數(shù)據(jù),JVM通過(guò)這個(gè)指針確定對(duì)象是哪個(gè)類(lèi)的實(shí)例。

如果應(yīng)用的對(duì)象過(guò)多,使用64位的指針將浪費(fèi)大量?jī)?nèi)存,統(tǒng)計(jì)而言,64位的JVM將會(huì)比32位的JVM多耗費(fèi)50%的內(nèi)存。為了節(jié)約內(nèi)存可以使用選項(xiàng)+UseCompressedOops開(kāi)啟指針壓縮。

開(kāi)啟該選項(xiàng)后,下列指針將壓縮至32位:每個(gè)Class的屬性指針(即靜態(tài)變量) 每個(gè)對(duì)象的屬性指針(即對(duì)象變量) 普通對(duì)象數(shù)組的每個(gè)元素指針

當(dāng)然,也不是所有的指針都會(huì)壓縮,一些特殊類(lèi)型的指針JVM不會(huì)優(yōu)化,比如指向PermGen的Class對(duì)象指針(JDK8中指向元空間的Class對(duì)象指針)、本地變量、堆棧元素、入?yún)ⅰ⒎祷刂岛蚇ULL指針等。

array length

如果對(duì)象是一個(gè)數(shù)組,那么對(duì)象頭還需要有額外的空間用于存儲(chǔ)數(shù)組的長(zhǎng)度,這部分?jǐn)?shù)據(jù)的長(zhǎng)度也是一個(gè)機(jī)器碼。64位JVM如果開(kāi)啟+UseCompressedOops選項(xiàng),該區(qū)域長(zhǎng)度也將由64位壓縮至32位。

對(duì)象體(實(shí)例數(shù)據(jù))

對(duì)象體存儲(chǔ)的是具體的成員屬性。

值得注意的是,如果成員屬性屬于普通對(duì)象類(lèi)型,則 oop 只存儲(chǔ)它的地址。

每個(gè)field在 oop 中都有一個(gè)對(duì)應(yīng)的偏移量(offset), oop 通過(guò)該偏移量得到該field的地址,再根據(jù)地址得到具體數(shù)據(jù)。

因此,Java對(duì)象中的field存儲(chǔ)的并不是對(duì)象本身,而是對(duì)象的地址。

JVM將Java對(duì)象的field存儲(chǔ)在 oop 的對(duì)象體中, oop 提供了一系列的方法來(lái)獲取和設(shè)置field,并且針對(duì)每種基礎(chǔ)類(lèi)型都提供了特有的實(shí)現(xiàn)。

對(duì)齊填充

由于虛擬機(jī)要求 對(duì)象起始地址必須是8字節(jié)的整數(shù)倍。

填充數(shù)據(jù)不是必須存在的,僅僅是為了字節(jié)對(duì)齊。

Klass繼承體系

與oop類(lèi)似,klass的繼承體系如下:

// hotspot/src/share/vm/oops/oopsHierarchy.hpp
...
class Klass;  // Klass繼承體系的最高父類(lèi)
class   InstanceKlass;  // 表示一個(gè)Java普通類(lèi),包含了一個(gè)類(lèi)運(yùn)行時(shí)的所有信息
class     InstanceMirrorKlass;  // 表示java.lang.Class
class     InstanceClassLoaderKlass; // 主要用于遍歷ClassLoader繼承體系
class     InstanceRefKlass;  // 表示java.lang.ref.Reference及其子類(lèi)
class   ArrayKlass;  // 表示一個(gè)Java數(shù)組類(lèi)
class     ObjArrayKlass;  // 普通對(duì)象的數(shù)組類(lèi)
class     TypeArrayKlass;  // 基礎(chǔ)類(lèi)型的數(shù)組類(lèi)
...

當(dāng)然,JVM本身所定義的用于描述Java類(lèi)的C++類(lèi)也使用klass去描述,這相當(dāng)于使用另一種面向?qū)ο蟮臋C(jī)制去描述C++類(lèi)這種本身便是面向?qū)ο蟮臄?shù)據(jù)。

(1)用于表示Java類(lèi)。klass包含元數(shù)據(jù)和方法信息,用來(lái)描述 Java 類(lèi)或者JVM內(nèi)部自帶的C++類(lèi)型信息。Java 類(lèi)的繼承信息、成員變量 、靜態(tài)變量 、成員方法 、構(gòu)造函數(shù)等信息都在 klass 中保存 ,一個(gè)class文件被JVM加載之后,就會(huì)被解析成一個(gè)klass對(duì)象存儲(chǔ)在內(nèi)存中。JVM據(jù)此在運(yùn)行期可以反射出Java類(lèi)的全部結(jié)構(gòu)信息。

(2)實(shí)現(xiàn)對(duì)象的虛分派(virtual dispatch)。所謂的虛分派,是JVM用來(lái)實(shí)現(xiàn)多態(tài)的一種機(jī)制。

體系總覽(待補(bǔ)充)

在JVM內(nèi)部定義了3種結(jié)構(gòu)去描述一種類(lèi)型 :oop 、klass 和 handle 類(lèi)。

注意,這 3 種數(shù)據(jù)結(jié)構(gòu)不僅能夠描述外在的 Java 類(lèi) ,也能夠描述 JVM內(nèi)在的C++類(lèi)型對(duì)象。

Handle是對(duì) oop 的行為的封裝(Handle類(lèi)內(nèi)部只有一個(gè)成員變量一handle,該變量類(lèi)型是oop*,因此該變量最終指向的就是一個(gè)oop的首地址),在訪問(wèn) Java 類(lèi)時(shí)一定是通過(guò) handle 內(nèi)部指針得到 oop 實(shí)例的,

再通過(guò) oop 就能拿到 klass ,如此 handle 最終便能操縱 oop 的行為了(注意,如果是調(diào)用JVM內(nèi)部C++類(lèi)型所對(duì)應(yīng)的oop的函數(shù) ,則不需要通過(guò) handle 來(lái)中轉(zhuǎn),直接通過(guò) oop 拿到指定的 klass便能實(shí)現(xiàn))。

klass 不僅包含自己所固有的行為接口,而且也能夠操作 Java 類(lèi)的函數(shù)。由于Java 函數(shù)在JVM內(nèi)部都被表示成虛函數(shù),因此handle模型其實(shí)就是 Java  類(lèi)行為的表達(dá)。

三者的關(guān)系如下:

Handle體系

handle封裝了oop,由于通過(guò)oop可以拿到 klass ,而 klass 是對(duì) Java 類(lèi)數(shù)據(jù)結(jié)構(gòu)和方法的描述 ,因此 handle 間接封裝了 klass。

JVM內(nèi)部使用一個(gè) table 來(lái)存儲(chǔ) oop 指針。但是JVM內(nèi)部采用這種結(jié)構(gòu)對(duì)klass進(jìn)行間接引用是為GC考慮。具體表現(xiàn)在2個(gè)地方 :

通過(guò)handle,能夠讓 GC 知道其內(nèi)部代碼都有哪些地方持有 GC 所管理的對(duì)象的引用,這只需要掃描 handle 所對(duì)應(yīng)的 table ,這樣 JVM 便無(wú)須關(guān)注其內(nèi)部到底哪些地方持有對(duì)普通對(duì)象的引用。

在GC過(guò)程中如果發(fā)生了對(duì)象移動(dòng)(例如從新生代移到了老年代),那么JVM的內(nèi)部引用無(wú)須跟著更改為被移動(dòng)對(duì)象的新地址,JVM 只需要更改 handle table 里對(duì)應(yīng)的指針即可 。

之所以分別給 oop 和 klass 定義了 2 套不同的 handle 體系,是為了方便垃圾回收。

本質(zhì)上,每一個(gè)oop,其實(shí)都是一個(gè) C++類(lèi)型,也即 klass;而對(duì)于每一個(gè) klass 所對(duì)應(yīng)的 class ,在JVM內(nèi)部又都會(huì)被封裝成 oop。

在具體描述一個(gè)類(lèi)型時(shí),會(huì)使用 oop 去存儲(chǔ)這個(gè)類(lèi)型的實(shí)例數(shù)據(jù),并使用 klass 去存儲(chǔ)這個(gè)類(lèi)型的元數(shù)據(jù)和虛方法表。而當(dāng)一個(gè)類(lèi)型完成其生命周期后,JVM會(huì)觸發(fā) GC 去回收,在回收時(shí),既要回收一個(gè)類(lèi)實(shí)例所對(duì)應(yīng)的實(shí)例數(shù)據(jù) oop , 也要回收其所對(duì)應(yīng)的元數(shù)據(jù)和虛方法表(當(dāng)然,兩者并不是同時(shí)回收,一個(gè)是堆區(qū)的垃圾回收, 一個(gè)是永久區(qū)的垃圾回收)。

為了讓 GC 既能回收 oop 也能回收 klass,因此 oop 本身被封裝成了 oop ,而 klass 也被封裝成 oop。

到此這篇關(guān)于HotSpot的Java對(duì)象模型之Oop-Klass模型詳解的文章就介紹到這了,更多相關(guān)HotSpot的Oop-Klass模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 從零開(kāi)始講解Java微信公眾號(hào)消息推送實(shí)現(xiàn)

    從零開(kāi)始講解Java微信公眾號(hào)消息推送實(shí)現(xiàn)

    微信公眾號(hào)分為訂閱號(hào)和服務(wù)號(hào),無(wú)論有沒(méi)有認(rèn)證,訂閱號(hào)每天都能推送一條消息,也就是每天只能推送一次消息給粉絲,這篇文章主要給大家介紹了關(guān)于Java微信公眾號(hào)消息推送實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Springboot中使用攔截器、過(guò)濾器、監(jiān)聽(tīng)器的流程分析

    Springboot中使用攔截器、過(guò)濾器、監(jiān)聽(tīng)器的流程分析

    Javaweb三大組件:servlet、Filter(過(guò)濾器)、?Listener(監(jiān)聽(tīng)器),這篇文章主要介紹了Springboot中使用攔截器、過(guò)濾器、監(jiān)聽(tīng)器的流程分析,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • springboottest測(cè)試依賴(lài)和使用方式

    springboottest測(cè)試依賴(lài)和使用方式

    這篇文章主要介紹了springboottest測(cè)試依賴(lài)和使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • win10 eclipse配置環(huán)境變量的教程圖解

    win10 eclipse配置環(huán)境變量的教程圖解

    本文通過(guò)圖文并茂的形式給大家介紹了win10 eclipse配置環(huán)境變量的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2018-07-07
  • springboot druid數(shù)據(jù)庫(kù)配置密碼加密的實(shí)現(xiàn)

    springboot druid數(shù)據(jù)庫(kù)配置密碼加密的實(shí)現(xiàn)

    Druid是阿里開(kāi)發(fā)的數(shù)據(jù)庫(kù)連接池,本文主要介紹了springboot druid數(shù)據(jù)庫(kù)配置密碼加密的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • Windows同時(shí)配置兩個(gè)jdk環(huán)境變量的操作步驟

    Windows同時(shí)配置兩個(gè)jdk環(huán)境變量的操作步驟

    Java Development Kit (JDK) 是開(kāi)發(fā)Java應(yīng)用程序的基礎(chǔ),包含了編譯器、調(diào)試器以及其他必要的工具,本指南將一步步指導(dǎo)您完成在Windows操作系統(tǒng)上同時(shí)配置兩個(gè)jdk環(huán)境變量的操作步驟,需要的朋友可以參考下
    2024-09-09
  • Java中雙冒號(hào)(::)運(yùn)算操作符用法詳解

    Java中雙冒號(hào)(::)運(yùn)算操作符用法詳解

    這篇文章主要給大家介紹了關(guān)于Java中雙冒號(hào)(::)運(yùn)算操作符用法的相關(guān)資料,雙冒號(hào)運(yùn)算操作符是類(lèi)方法的句柄,lambda表達(dá)式的一種簡(jiǎn)寫(xiě),這種簡(jiǎn)寫(xiě)的學(xué)名叫eta-conversion或者叫η-conversion,需要的朋友可以參考下
    2023-11-11
  • HttpClient實(shí)現(xiàn)表單提交上傳文件

    HttpClient實(shí)現(xiàn)表單提交上傳文件

    這篇文章主要為大家詳細(xì)介紹了HttpClient實(shí)現(xiàn)表單提交上傳文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 解決Feign切換client到okhttp無(wú)法生效的坑(出現(xiàn)原因說(shuō)明)

    解決Feign切換client到okhttp無(wú)法生效的坑(出現(xiàn)原因說(shuō)明)

    這篇文章主要介紹了解決Feign切換client到okhttp無(wú)法生效的坑(出現(xiàn)原因說(shuō)明),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • MyBatis無(wú)縫對(duì)接Spring的方法

    MyBatis無(wú)縫對(duì)接Spring的方法

    Spring框架與MyBatis框架是Java互聯(lián)網(wǎng)技術(shù)的主流框架。那么mybatis如何無(wú)縫對(duì)接spring呢?下面通過(guò)本文給大家介紹,需要的的朋友參考下吧
    2017-09-09

最新評(píng)論