jvm源码解读--15 oop对象详解

Posted 张三f

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jvm源码解读--15 oop对象详解相关的知识,希望对你有一定的参考价值。

(gdb) p obj 
$15 = (oopDesc *) 0xf3885d08
(gdb) p * obj 
$16 = {
  _mark = 0x70dea4e01, 
  _metadata = {
    _klass = 0x2000070e, 
    _compressed_klass = 536872718
  }, 
  static _bs = 0x7f658801eea8
}

1.介绍oo对象

现在先看最长使用的oop对象的定义,jvm的二分模型就是oop和class,所有的对象在hotspot层全被具体化成了oop对象,

oop的结构很简单,表示一个hotspot对象,也可以理解为一个java对象(不过包了一层handle才是java对象), 比如在java中创建一个对象

Student xm=new Student();

xm.name="小明";

xm.age=18;

这个xm就是oop,,这个oop被放在堆中,代表的就是xm这个对象,那么这个对象有成员变量name,age,那么oop对象在内存中的

oop对象
内存地址64位 _mark属性
内存地址32(压缩指针)或64 _metadata,指向class对象
内存地址 "小明"oop对象指针
内存地址 18,int类型数值

那么整个对象的大小包括了oop对象头大小为12字节(压缩)+成员变量1+成员变量2..,其中包括父类的成员变量的值,不包括static变量的值(static变量保存在class的java_mirror属性中)

如果新建一个oop对象,分配内存需要计算oop大小,那么oop的size()实际上是取值oop对应的元对象class的_layout_helper 的大小

贴一个class对象

(gdb) p k
$87 = (InstanceKlass *) 0x100060030
        (gdb) p * k
$88 = (InstanceKlass) {
        <Klass> = {
            <Metadata> = {
                <MetaspaceObj> = {<No data fields>},
                members of Metadata:
                _vptr.Metadata = 0x7f5aa945e590 <vtable for InstanceKlass+16>,
                        _valid = 0
            },
            members of Klass:
            _layout_helper = 24,
            _super_check_offset = 56,
            _name = 0x7f5aa014b5d8,
            _secondary_super_cache = 0x0,
            _secondary_supers = 0x7f5aa5399090,
            _primary_supers = {0x100000f30, 0x100060030, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
            _java_mirror = 0xd758fc70,
            _super = 0x100000f30,
            _subklass = 0x0,
            _next_sibling = 0x1000471a8,
            _next_link = 0x0,
            _class_loader_data = 0x7f5aa0153768,
            _modifier_flags = 1,
            _access_flags = {
                    _flags = 2097185
            },
            _last_biased_lock_bulk_revocation_time = 0,
            _prototype_header = 0x5,
            _biased_lock_revocation_count = 0,
            _modified_oops = 1 \'\\001\',
                    _accumulated_modified_oops = 0 \'\\000\'
        },
        members of InstanceKlass:
        static _total_instanceKlass_count = 415,
        _annotations = 0x0,
        _array_klasses = 0x0,
        _constants = 0x7f5aa5799080,
        _inner_classes = 0x7f5aa5399060,
        _source_debug_extension = 0x0,
        _array_name = 0x0,
        _nonstatic_field_size = 2,
        _static_field_size = 1,
        _generic_signature_index = 0,
        _source_file_name_index = 69,
        _static_oop_field_count = 0,
        _java_fields_count = 3,
        _nonstatic_oop_map_size = 1,
        _is_marked_dependent = false,
        _misc_flags = 6,
        _minor_version = 0,
        _major_version = 51,
        _init_thread = 0x0,
        _vtable_len = 5,
        _itable_len = 2,
        _oop_map_cache = 0x0,
        _member_names = 0x0,
        _jni_ids = 0x0,
        _methods_jmethod_ids = 0x0,
        _dependencies = 0x0,
        _osr_nmethods_head = 0x0,
        _breakpoints = 0x0,
        _previous_versions = 0x0,
        _cached_class_file = 0x0,
        _idnum_allocated_count = 3,
        _init_state = 1 \'\\001\',
        _reference_type = 0 \'\\000\',
        _jvmti_cached_class_field_map = 0x0,
        _verify_count = 0,
        _methods = 0x7f5aa5799338,
        _default_methods = 0x0,
        _local_interfaces = 0x7f5aa5399090,
        _transitive_interfaces = 0x7f5aa5399090,
        _method_ordering = 0x7f5aa5399048,
        _default_vtable_indices = 0x0,
        _fields = 0x7f5aa5799308
}

这个类为

(gdb) p name->as_utf8()
$80 = 0x7f5aa0009c88 "com/test/Test"

2.介绍mark成员变量

这里先贴出来注释的东西

// 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.

// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)

unused:25  hash:31  unused:1   age:4 biased_lock:1  lock:2
  哈希值   gc存活年度 偏向锁 状态
 

111 0000 1101 1110 1010 0100 1110 

(70D EA4E)

0 000 0 0 01

 

_mark = 0x70dea4e01,变为二进制

111 0000 1101 1110 1010 0100 1110 0000 0001

对于lock:2的取值不同,代表的意义不同,当为01的时候,不锁,那么按照上表表头来解析,如果是11,那么是在gc的时候会将已经已经复制到to区域的oop的,在没有清除之前,他会在eden区域,这个时候会将eden区域的老oop对象后两位设置为11; (used by markSweep to mark an object not valid at any other time)

//    [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

那么现在,现在执行new Object().hasCode()方法就是,取得oop的hash值的执行过程为

JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
  JVMWrapper("JVM_IHashCode");
  // as implemented in the classic virtual machine; return 0 if object is NULL
  return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END

进入

intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {
  if (UseBiasedLocking) {
// thread-local storage.
    if (obj->mark()->has_bias_pattern()) {
      // Box and unbox the raw reference just in case we cause a STW safepoint.
      Handle hobj (Self, obj) ;
      // Relaxing assertion for bug 6320749.
      assert (Universe::verify_in_progress() ||
              !SafepointSynchronize::is_at_safepoint(),
             "biases should not be seen by VM thread here");
      BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());
      obj = hobj() ;
      assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
    }
  }

  ObjectMonitor* monitor = NULL;
  markOop temp, test;
  intptr_t hash;
  markOop mark = ReadStableMark (obj);

  // object should remain ineligible for biased locking
  assert (!mark->has_bias_pattern(), "invariant") ;

  if (mark->is_neutral()) {
    hash = mark->hash();              // this is a normal header
    if (hash) {                       // if it has hash, just return it
      return hash;
    }
    hash = get_next_hash(Self, obj);  // allocate a new hash code
    temp = mark->copy_set_hash(hash); // merge the hash code into header
    // use (machine word version) atomic operation to install the hash
    test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);
    if (test == mark) {
      return hash;
    }

接着

  // hash operations
  intptr_t hash() const {
    return mask_bits(value() >> hash_shift, hash_mask);
  }

看 (gdb) p/d hash_shift   $18 = 8

那么  uintptr_t value() const { return (uintptr_t) this; } 那么就是将0x70dea4e01,右移8位 

在看掩码

(gdb) p/x hash_mask
$20 = 7FFF FFFF

二进制位01111111111111111111111111111111,那么31位1,就和hash的31为对应上了

那么打印一下计算结果

(gdb) p/x hash 
$22 = 0x70dea4e

所以:这个值和(70D EA4E) 表格中的hash值是一样的

以上是关于jvm源码解读--15 oop对象详解的主要内容,如果未能解决你的问题,请参考以下文章

JVM源码分析之SystemGC完全解读

OpenHarmony-内核对象事件之源码详解

JVM中的压缩OOP

HSDB: 16张图带你看见JVM中的Java对象

JVM源码分析之栈溢出完全解读

JVM源码分析之临门一脚的OutOfMemoryError完全解读