Java對(duì)象結(jié)構(gòu)

概述

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

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

header.png
compressed_header.png

可以看到

  • 開(kāi)啟指針壓縮時(shí),markword占用8bytes,類(lèi)型指針占用8bytes,共占用16bytes;
  • 未開(kāi)啟指針壓縮時(shí),markword占用8bytes,類(lèi)型指針占用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ì)象類(lèi)型指針為0xf800c005,但實(shí)際的類(lèi)型指針為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 偏向鎖 線(xiàn)程ID(54),時(shí)間戳(2),年齡(4)
無(wú) 00 輕量級(jí)鎖 棧中鎖記錄的指針(64)
無(wú) 10 重量級(jí)鎖 monitor的指針(64)
無(wú) 11 GC標(biāo)記 空,不需要記錄信息

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

  • 如果對(duì)象沒(méi)有重寫(xiě)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í),記錄的為獲得偏向鎖的線(xiàn)程指針,該指針也是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)有這么一句話(huà):

// 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)建線(xiàn)程時(shí),線(xiàn)程地址會(huì)進(jìn)行對(duì)齊處理,保證低10位為0

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

實(shí)例數(shù)據(jù)中主要包括對(duì)象的各種成員變量,包括基本類(lèi)型和引用類(lèi)型;static類(lèi)型的變量會(huì)放到j(luò)ava/lang/Class中,而不會(huì)放到實(shí)例數(shù)據(jù)中;
對(duì)于引用類(lèi)型的成員(包括string),存儲(chǔ)的指針;對(duì)于基本類(lèi)型,直接存儲(chǔ)內(nèi)容;通常會(huì)將基本類(lèi)型存儲(chǔ)在一起,引用類(lèi)型存儲(chǔ)在一起;
例如類(lèi)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為基本類(lèi)型,存儲(chǔ)在一起;其它的引用類(lèi)型存儲(chǔ)在一起;int占用4bytes,不足8bytes,自動(dòng)補(bǔ)足到8bytes;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容