亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談java對(duì)象結(jié)構(gòu) 對(duì)象頭 Markword

 更新時(shí)間:2020年10月19日 11:26:07   作者:allanYan  
這篇文章主要介紹了淺談java對(duì)象結(jié)構(gòu) 對(duì)象頭 Markword,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

概述

對(duì)象實(shí)例由對(duì)象頭、實(shí)例數(shù)據(jù)組成,其中對(duì)象頭包括markword和類型指針,如果是數(shù)組,還包括數(shù)組長(zhǎng)度;

| 類型 | 32位JVM | 64位JVM|
| ------ ---- | ------------| --------- |
| markword | 32bit | 64bit |
| 類型指針 | 32bit |64bit ,開(kāi)啟指針壓縮時(shí)為32bit |
| 數(shù)組長(zhǎng)度 | 32bit |32bit |

header.png

compressed_header.png

可以看到

開(kāi)啟指針壓縮時(shí),markword占用8bytes,類型指針占用8bytes,共占用16bytes;

未開(kāi)啟指針壓縮時(shí),markword占用8bytes,類型指針占用4bytes,但由于java內(nèi)存地址按照8bytes對(duì)齊,長(zhǎng)度必須是8的倍數(shù),因此會(huì)從12bytes補(bǔ)全到16bytes;

數(shù)組長(zhǎng)度為4bytes,同樣會(huì)進(jìn)行對(duì)齊,補(bǔ)足到8bytes;

另外從上面的截圖可以看到,開(kāi)啟指針壓縮之后,對(duì)象類型指針為0xf800c005,但實(shí)際的類型指針為0x7c0060028;那么指針是如何壓縮的呢?

實(shí)際上由于java地址一定是8的倍數(shù),因此將0xf800c005*8即可得到實(shí)際的指針0x7c0060028,關(guān)于指針壓縮的更多知識(shí)可參考官方文檔。

markword結(jié)構(gòu)

markword的結(jié)構(gòu),定義在markOop.hpp文件:

 32 bits:
 --------
 hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
 JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
 size:32 ------------------------------------------>| (CMS free block)
 PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

 64 bits:
 --------
 unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
 JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
 PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
 size:64 ----------------------------------------------------->| (CMS free block)

 unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
 JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
 narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
 unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
 [ptr    | 00] locked    ptr points to real header on stack
 [header  | 0 | 01] unlocked   regular object header
 [ptr    | 10] monitor   inflated lock (header is wapped out)
 [ptr    | 11] marked    used by markSweep to mark an object

由于目前基本都在使用64位JVM,此處不再對(duì)32位的結(jié)構(gòu)進(jìn)行詳細(xì)說(shuō)明:

偏向鎖標(biāo)識(shí)位 鎖標(biāo)識(shí)位 鎖狀態(tài) 存儲(chǔ)內(nèi)容
0 01 未鎖定 hash code(31),年齡(4)
1 01 偏向鎖 線程ID(54),時(shí)間戳(2),年齡(4)
無(wú) 00 輕量級(jí)鎖 棧中鎖記錄的指針(64)
無(wú) 10 重量級(jí)鎖 monitor的指針(64)
無(wú) 11 GC標(biāo)記 空,不需要記錄信息

此處,有幾點(diǎn)要注意:

如果對(duì)象沒(méi)有重寫hashcode方法,那么默認(rèn)是調(diào)用os::random產(chǎn)生hashcode,可以通過(guò)System.identityHashCode獲取;os::random產(chǎn)生hashcode的規(guī)則為:next_rand = (16807seed) mod (2*31-1),因此可以使用31位存儲(chǔ);另外一旦生成了hashcode,JVM會(huì)將其記錄在markword中;

GC年齡采用4位bit存儲(chǔ),最大為15,例如MaxTenuringThreshold參數(shù)默認(rèn)值就是15;

當(dāng)處于輕量級(jí)鎖、重量級(jí)鎖時(shí),記錄的對(duì)象指針,根據(jù)JVM的說(shuō)明,此時(shí)認(rèn)為指針仍然是64位,最低兩位假定為0;當(dāng)處于偏向鎖時(shí),記錄的為獲得偏向鎖的線程指針,該指針也是64位;

 We assume that stack/thread pointers have the lowest two bits cleared.
ObjectMonitor* monitor() const {
 assert(has_monitor(), "check");
 // Use xor instead of &~ to provide one extra tag-bit check.
 return (ObjectMonitor*) (value() ^ monitor_value);//monitor_value=2,value最右兩位為10,因此異或之后最右兩位為0
 }
JavaThread* biased_locker() const {
 assert(has_bias_pattern(), "should not call this otherwise");
 return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place))));
//~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place)為11111111111111111111110010000000,計(jì)算后的結(jié)果中,低10位全部為0;
 }

由于java中內(nèi)存地址都是8的倍數(shù),因此可以理解為最低3bit為0,因此假設(shè)輕量級(jí)和重量級(jí)鎖的最低2位為0是成立的;但為什么偏向鎖的最低10位都是0?查看markOop.hpp文件,發(fā)現(xiàn)有這么一句話:

// Alignment of JavaThread pointers encoded in object header required by biased locking
 enum { biased_lock_alignment = 2 << (epoch_shift + epoch_bits)
//epoch_shift+epoch_bits=10
 };

thread.hpp中重載了operator new:

void* operator new(size_t size) { return allocate(size, true); }

// ======= Thread ========
// Support for forcing alignment of thread objects for biased locking
void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {
 if (UseBiasedLocking) {
 const int alignment = markOopDesc::biased_lock_alignment;//10
 size_t aligned_size = size + (alignment - sizeof(intptr_t));
 void* real_malloc_addr = throw_excpt? AllocateHeap(aligned_size, flags, CURRENT_PC)
           : os::malloc(aligned_size, flags, CURRENT_PC);
 void* aligned_addr  = (void*) align_size_up((intptr_t) real_malloc_addr, alignment);
 assert(((uintptr_t) aligned_addr + (uintptr_t) size) <=
   ((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size),
   "JavaThread alignment code overflowed allocated storage");
 if (TraceBiasedLocking) {
  if (aligned_addr != real_malloc_addr)
  tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT,
      real_malloc_addr, aligned_addr);
 }
 ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr;
 return aligned_addr;
 } else {
 return throw_excpt? AllocateHeap(size, flags, CURRENT_PC)
      : os::malloc(size, flags, CURRENT_PC);
 }
}

如果開(kāi)啟了偏移鎖,在創(chuàng)建線程時(shí),線程地址會(huì)進(jìn)行對(duì)齊處理,保證低10位為0

實(shí)例數(shù)據(jù)

實(shí)例數(shù)據(jù)中主要包括對(duì)象的各種成員變量,包括基本類型和引用類型;static類型的變量會(huì)放到j(luò)ava/lang/Class中,而不會(huì)放到實(shí)例數(shù)據(jù)中;

對(duì)于引用類型的成員(包括string),存儲(chǔ)的指針;對(duì)于基本類型,直接存儲(chǔ)內(nèi)容;通常會(huì)將基本類型存儲(chǔ)在一起,引用類型存儲(chǔ)在一起;

例如類Test的成員定義如下:

 private static Test t1=new Test();
 private Test t2;
 private int a=5;
 private Integer b=7;
 private String c="112";
 private BigDecimal d=new BigDecimal("5");
 private long e=9l;

body.png

可以看到long e、int a為基本類型,存儲(chǔ)在一起;其它的引用類型存儲(chǔ)在一起;int占用4bytes,不足8bytes,自動(dòng)補(bǔ)足到8bytes;

補(bǔ)充知識(shí):java的對(duì)象物理結(jié)構(gòu),以及對(duì)象頭中MarkWord與鎖的關(guān)系

java 對(duì)象頭

我們都知道,Java對(duì)象存儲(chǔ)在堆(Heap)內(nèi)存。那么一個(gè)Java對(duì)象到底包含什么呢?概括起來(lái)分為對(duì)象頭、對(duì)象體和對(duì)齊字節(jié)。

如下圖所示:

對(duì)象的幾個(gè)部分的作用:

1.對(duì)象頭中的Mark Word(標(biāo)記字)主要用來(lái)表示對(duì)象的線程鎖狀態(tài),另外還可以用來(lái)配合GC、存放該對(duì)象的hashCode;

2.Klass Word是一個(gè)指向方法區(qū)中Class信息的指針,意味著該對(duì)象可隨時(shí)知道自己是哪個(gè)Class的實(shí)例;

3.數(shù)組長(zhǎng)度也是占用64位(8字節(jié))的空間,這是可選的,只有當(dāng)本對(duì)象是一個(gè)數(shù)組對(duì)象時(shí)才會(huì)有這個(gè)部分;

4.對(duì)象體是用于保存對(duì)象屬性和值的主體部分,占用內(nèi)存空間取決于對(duì)象的屬性數(shù)量和類型;

5.對(duì)齊字是為了減少堆內(nèi)存的碎片空間(不一定準(zhǔn)確)。

了解了對(duì)象的總體結(jié)構(gòu),接下來(lái)深入地了解對(duì)象頭的三個(gè)部分。

一、Mark Word(標(biāo)記字)

以上是Java對(duì)象處于5種不同狀態(tài)時(shí),Mark Word中64個(gè)位的表現(xiàn)形式,上面每一行代表對(duì)象處于某種狀態(tài)時(shí)的樣子。其中各部分的含義如下:

lock:2位的鎖狀態(tài)標(biāo)記位,由于希望用盡可能少的二進(jìn)制位表示盡可能多的信息,所以設(shè)置了lock標(biāo)記。該標(biāo)記的值不同,整個(gè)Mark Word表示的含義不同。biased_lock和lock一起,表達(dá)的鎖狀態(tài)含義如下:

biased_lock:對(duì)象是否啟用偏向鎖標(biāo)記,只占1個(gè)二進(jìn)制位。為1時(shí)表示對(duì)象啟用偏向鎖,為0時(shí)表示對(duì)象沒(méi)有偏向鎖。lock和biased_lock共同表示對(duì)象處于什么鎖狀態(tà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:31位的對(duì)象標(biāo)識(shí)hashCode,采用延遲加載技術(shù)。調(diào)用方法System.identityHashCode()計(jì)算,并會(huì)將結(jié)果寫到該對(duì)象頭中。當(dāng)對(duì)象加鎖后(偏向、輕量級(jí)、重量級(jí)),MarkWord的字節(jié)沒(méi)有足夠的空間保存hashCode,因此該值會(huì)移動(dòng)到管程Monitor中。

thread:持有偏向鎖的線程ID。

epoch:偏向鎖的時(shí)間戳。

ptr_to_lock_record:輕量級(jí)鎖狀態(tài)下,指向棧中鎖記錄的指針。

ptr_to_heavyweight_monitor:重量級(jí)鎖狀態(tài)下,指向?qū)ο蟊O(jiān)視器Monitor的指針。

二、Klass Word(類指針)

這一部分用于存儲(chǔ)對(duì)象的類型指針,該指針指向它的類元數(shù)據(jù),JVM通過(guò)這個(gè)指針確定對(duì)象是哪個(gè)類的實(shí)例。該指針的位長(zhǎng)度為JVM的一個(gè)字大小,即32位的JVM為32位,64位的JVM為64位。

如果應(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)啟指針壓縮,其中,oop即ordinary object pointer普通對(duì)象指針。

開(kāi)啟該選項(xiàng)后,下列指針將壓縮至32位:

每個(gè)Class的屬性指針(即靜態(tài)變量)

每個(gè)對(duì)象的屬性指針(即對(duì)象變量)

普通對(duì)象數(shù)組的每個(gè)元素指針

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

三、數(shù)組長(zhǎng)度

如果對(duì)象是一個(gè)數(shù)組,那么對(duì)象頭還需要有額外的空間用于存儲(chǔ)數(shù)組的長(zhǎng)度,這部分?jǐn)?shù)據(jù)的長(zhǎng)度也隨著JVM架構(gòu)的不同而不同:32位的JVM上,長(zhǎng)度為32位;64位JVM則為64位。

64位JVM如果開(kāi)啟+UseCompressedOops選項(xiàng),該區(qū)域長(zhǎng)度也將由64位壓縮至32位。

以上這篇淺談java對(duì)象結(jié)構(gòu) 對(duì)象頭 Markword就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java并發(fā)編程之CountDownLatch源碼解析

    Java并發(fā)編程之CountDownLatch源碼解析

    這篇文章主要介紹了Java并發(fā)編程之CountDownLatch源碼解析,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java并發(fā)編程的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-04-04
  • MyBatis中如何查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù)

    MyBatis中如何查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù)

    這篇文章主要介紹了MyBatis中如何查詢某個(gè)時(shí)間段內(nèi)的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • JavaWeb實(shí)現(xiàn)注冊(cè)用戶名檢測(cè)

    JavaWeb實(shí)現(xiàn)注冊(cè)用戶名檢測(cè)

    這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)注冊(cè)用戶名檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Spring6?的JdbcTemplate的JDBC模板類的使用介紹(最新推薦)

    Spring6?的JdbcTemplate的JDBC模板類的使用介紹(最新推薦)

    JdbcTemplate?是Spring?提供的一個(gè)JDBC模板類,是對(duì)JDBC的封裝,簡(jiǎn)化JDBC代碼,當(dāng)然,你也可以不用,可以讓Spring集成其它的ORM框架,這篇文章主要介紹了Spring6?的JdbcTemplate的JDBC模板類的詳細(xì)使用說(shuō)明,需要的朋友可以參考下
    2024-05-05
  • SpringBoot整合WebService服務(wù)的實(shí)現(xiàn)代碼

    SpringBoot整合WebService服務(wù)的實(shí)現(xiàn)代碼

    WebService是一個(gè)SOA(面向服務(wù)的編程)的架構(gòu),它是不依賴于語(yǔ)言,不依賴于平臺(tái),可以實(shí)現(xiàn)不同的語(yǔ)言間的相互調(diào)用,通過(guò)Internet進(jìn)行基于Http協(xié)議的網(wǎng)絡(luò)應(yīng)用間的交互,這篇文章主要介紹了SpringBoot整合WebService服務(wù)的實(shí)例代碼,需要的朋友可以參考下
    2022-02-02
  • 如何修改覆蓋spring boot默認(rèn)日志策略logback詳解

    如何修改覆蓋spring boot默認(rèn)日志策略logback詳解

    這篇文章主要給大家介紹了關(guān)于如何修改覆蓋spring boot默認(rèn)日志策略logback的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • Java如何獲取客戶端mac地址

    Java如何獲取客戶端mac地址

    在用戶登錄時(shí),通過(guò)獲取IP地址來(lái)識(shí)別計(jì)算機(jī)的MAC地址,然后將用戶賬號(hào)與該MAC地址進(jìn)行綁定,確保每個(gè)賬號(hào)只能在一臺(tái)特定的計(jì)算機(jī)上登錄,增強(qiáng)系統(tǒng)安全性,這種方法適用于需要嚴(yán)格賬戶安全管理的場(chǎng)景
    2024-09-09
  • java代碼mqtt接收發(fā)送消息方式

    java代碼mqtt接收發(fā)送消息方式

    這篇文章主要介紹了java代碼mqtt接收發(fā)送消息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Red?Hat?安裝JDK與IntelliJ?IDEA的詳細(xì)過(guò)程

    Red?Hat?安裝JDK與IntelliJ?IDEA的詳細(xì)過(guò)程

    YUM是基于Red Hat的Linux發(fā)行版的一個(gè)強(qiáng)大而用戶友好的包管理工具,這篇文章主要介紹了Red?Hat安裝JDK與IntelliJ IDEA,需要的朋友可以參考下
    2023-08-08
  • 創(chuàng)建動(dòng)態(tài)代理對(duì)象bean,并動(dòng)態(tài)注入到spring容器中的操作

    創(chuàng)建動(dòng)態(tài)代理對(duì)象bean,并動(dòng)態(tài)注入到spring容器中的操作

    這篇文章主要介紹了創(chuàng)建動(dòng)態(tài)代理對(duì)象bean,并動(dòng)態(tài)注入到spring容器中的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02

最新評(píng)論