JVM对象头的简单记录

6 篇文章 0 订阅

JVM对象头的简单记录

对象头示意图

  • 对象结构总览

    结构描述
    header对象头
    instance data实例信息
    padding对齐信息
    • JVM要求对象的大小为8byte的整数倍,padding用于补齐数据,所以可能有可能没有
    • 对齐8byte的原因:为了实现的简单与高效。一个class的每个field都会分配“byte offset”,再对每个单独的byte进行读/写访问。如果不对齐byte,则要记录“sub-byte offsets”,会导致额外的访问逻辑。直接按byte访问则更简单、高效。
  • 对象头(32位)

    32bits32bits32bits
    Mark WordKlass Wordarray length
    • 如果不是数组,则没有array length信息
  • MarkWord 结构图 (32位)

    25bits

    4bits

    1bits

    2bits

    锁状态

    23bits

    2bits

    是否偏向锁

    锁标志位

    HashCode

    分代年龄

    0

    01

    无锁

    Thread ID

    Epoch

    分代年龄

    1

    01

    偏向锁

    指向栈中锁记录的指针

    00

    轻量级锁

    指向重量级锁的指针

    10

    重量级锁

    GC标记信息(CMS)

    11

    GC标记

  • MarkWord 结构图 (64位)

    25bits31bit1bits4bits1bits(偏向位)2bits(锁标志位)锁状态
    unusedhashcodeunused分代年龄001无锁
    54bits2bit1bits4bits1bits(偏向位)2bits(锁标志位)锁状态
    当前Thread指针Epochunused分代年龄101偏向锁
    62bits2bits(锁标志位)锁状态
    指向栈中锁记录的指针00轻量级锁
    指向重量级锁的指针10重量级锁
    GC标记信息(CMS)11GC标记
  • MarkWord 结构,具体请查看后面附的“对象头描述源码”

对象头信息描述

  • Mark Word
    • 锁标志位:用于JVM判定对象是否被锁住,以及锁膨胀优化。包括
      • 无锁:新建一个对象的默认状态
      • 偏向锁:只需比较 Thread ID,适用于单个线程重入
      • 轻量级锁:CAS自旋,速度快,但存在CPU空转问题
      • 重量级锁:需调用系统级互斥锁(mutex/monitor),效率低
      • GC标记:由markSweep使用,标记一个对象为无效状态
    • 分代年龄:即在young区存活的次数,默认达到15次后进入old区(CMS默认6次),4bits最大值为15(32位/64位一样)
    • HashCode:调用 System.identityHashCode(…) 获得,HotSpot使用xor-shift算法
    • Thread ID:偏向锁偏向的线程id
    • Epoch:用于保存偏向时间戳
  • Klass Word:即class指针(指向内存中对象的class类,例如通过getClass拿到类信息)
  • 数组长度:32位、64位进程中占用32bits(4字节)的空间,只有当对象是一个数组对象时才会有这部分(这就解释了为什么数组对象的最大长度小于int最大值)

想直接看看对象信息?

  • Maven导包 pom.xml
    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
    </dependency>
    
  • 代码示例
    Object obj = new Object();
    ClassLayout layout = ClassLayout.parseInstance(obj);
    System.out.println(layout.toPrintable());
    
  • 打印的信息(默认: -XX:+UseCompressedClassPointers,你可以关闭它看看变化)
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    • 前2个object header是MarkWord
    • 第3个object header是KlassWord
    • 最后4个byte是 Padding,保证对象大小为8byte的整数倍

附:对象头描述(OpenJDK JVM源码)

  • oop.hpp
    // oopDesc is the top baseclass for objects classes.  The {name}Desc classes describe
    // the format of Java objects so the fields can be accessed from C++.
    // oopDesc is abstract.
    ...省略...
    class oopDesc {
      friend class VMStructs;
     private:
      volatile markOop  _mark;
      union _metadata {
        Klass*      _klass;
        narrowKlass _compressed_klass;
      } _metadata;
      ...省略...
    }
    
  • markOop.hpp
    // The markOop describes the header of an object.  
    //  
    // Note that the mark is not a real oop but just a word.  
    // It is placed in the oop hierarchy for historical reasons.  
    //  
    // Bit-format of an object header (most significant first, big endian layout below):  
    //  
    //  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)  
    //  
    //  - hash contains the identity hash value: largest value is  
    //    31 bits, see os::random().  Also, 64-bit vm's require  
    //    a hash value no bigger than 32 bits because they will not  
    //    properly generate a mask larger than that: see library_call.cpp  
    //    and c1_CodePatterns_sparc.cpp.  
    //  
    //  - the biased lock pattern is used to bias a lock toward a given  
    //    thread. When this pattern is set in the low three bits, the lock  
    //    is either biased toward a given thread or "anonymously" biased,  
    //    indicating that it is possible for it to be biased. When the  
    //    lock is biased toward a given thread, locking and unlocking can  
    //    be performed by that thread without using atomic operations.  
    //    When a lock's bias is revoked, it reverts back to the normal  
    //    locking scheme described below.  
    //  
    //    Note that we are overloading the meaning of the "unlocked" state  
    //    of the header. Because we steal a bit from the age we can  
    //    guarantee that the bias pattern will never be seen for a truly  
    //    unlocked object.  
    //  
    //    Note also that the biased state contains the age bits normally  
    //    contained in the object header. Large increases in scavenge  
    //    times were seen when these bits were absent and an arbitrary age  
    //    assigned to all biased objects, because they tended to consume a  
    //    significant fraction of the eden semispaces and were not  
    //    promoted promptly, causing an increase in the amount of copying  
    //    performed. The runtime system aligns all JavaThread* pointers to  
    //    a very large value (currently 128 bytes (32bVM) or 256 bytes (64bVM))  
    //    to make room for the age bits & the epoch bits (used in support of  
    //    biased locking), and for the CMS "freeness" bit in the 64bVM (+COOPs).  
    //  
    //    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread  
    //    [0           | epoch | age | 1 | 01]       lock is anonymously biased  
    //  
    //  - the two lock bits are used to describe three states: locked/unlocked and monitor.  
    //  
    //    [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  
    //                                               not valid at any other time  
    //  
    //    We assume that stack/thread pointers have the lowest two bits cleared.  
    

附:数组最大长度的判定(OpenJDK JVM源码)

  • arrayKlass.cpp
    objArrayOop arrayKlass::allocate_arrayArray(int n, int length, TRAPS) {  
    	if (length < 0) {  
    	  THROW_0(vmSymbols::java_lang_NegativeArraySizeException());  
    	}  
    	if (length > arrayOopDesc::max_array_length(T_ARRAY)) {  
    	  report_java_out_of_memory("Requested array size exceeds VM limit");  
    	  JvmtiExport::post_array_size_exhausted();  
    	  THROW_OOP_0(Universe::out_of_memory_error_array_size());  
    	}  
    	// ......  
    }  
    
  • arrayOop.hpp
    // Return the maximum length of an array of BasicType.  The length can passed  
    // to typeArrayOop::object_size(scale, length, header_size) without causing an  
    // overflow. We also need to make sure that this will not overflow a size_t on  
    // 32 bit platforms when we convert it to a byte size.  
    static int32_t max_array_length(BasicType type) {  
      assert(type >= 0 && type < T_CONFLICT, "wrong type");  
      assert(type2aelembytes(type) != 0, "wrong type");  
      
      const size_t max_element_words_per_size_t =  
        align_size_down((SIZE_MAX/HeapWordSize - header_size(type)), MinObjAlignment);  
      const size_t max_elements_per_size_t =  
        HeapWordSize * max_element_words_per_size_t / type2aelembytes(type);  
      if ((size_t)max_jint < max_elements_per_size_t) {  
        // It should be ok to return max_jint here, but parts of the code  
        // (CollectedHeap, Klass::oop_oop_iterate(), and more) uses an int for  
        // passing around the size (in words) of an object. So, we need to avoid  
        // overflowing an int when we add the header. See CRs 4718400 and 7110613.  
        return align_size_down(max_jint - header_size(type), MinObjAlignment);  
      }  
      return (int32_t)max_elements_per_size_t;  
    } 
    
  • sizecalc.h (32位系统 size_t=4字节,64位系统 size_t=8字节)
    #include <stdint.h> /* SIZE_MAX for C99+ */  
    /* http://stackoverflow.com/questions/3472311/what-is-a-portable-method-to-find-the-maximum-value-of-size-t */  
    #ifndef SIZE_MAX  
    #define SIZE_MAX ((size_t)-1)  
    #endif 
    
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值