java 对象头信息 (二)

java对象的布局以及对象头的布局
**1、JOL来分析java的对象布局 **
首先添加JOL的依赖


        
            org.openjdk.jol
            jol-core
            0.8
        

A.java

public class A {
   //没有任何字段
}

JOLExample1.java

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.vm.VM;

import static java.lang.System.out;

public class JOLExample1 {
    static  A a = new A();
    public static void main(String[] args) {
        //jvm的信息
        out.println(VM.current().details());
        out.println(ClassLayout.parseInstance(a).toPrintable());
    }
}

运行结果

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

结果分析1
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
对应:
[Oop(Ordinary Object Pointer), boolean, byte, char, short, int, float, long, double]大小

整个对象一共16Byte,其中对象头(Object header)12Byte,还有4Byte是对齐的字节(因为在64位虚拟机上对象的大小必 须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据为0Byte?两个问题
1、什么叫做对象的实例数据呢?
2、那么对象头里面的12B到底存的是什么呢? 首先要明白什么对象的实例数据很简单,我们可以在A当中添加一个boolean的字段,大家都知道boolean字段占 1B,然后再看结果

A.java

public class A {
  boolean flag =false;
}

运行结果2

# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

 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)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     1   boolean A.flag                                    false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

分析结果2
整个对象的大小还是没有改变一共16Byte,其中对象头(Object header)12Byte,boolean字段flag(对象的实例数据)占 1Byte、剩下的3Byte就是对齐字节。由此我们可以认为一个对象的布局大体分为三个部分分别是对象头(Object header)、 对象的实例数据字节对齐 接下来讨论第二个问题,对象头为什么是12Byte?这个12Byte当中分别存储的是什么呢?(不同位数的VM对象头的长度不一 样,这里指的是64bit的vm) 关于java对象头的一些专业术语 http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.htm 首先引用openjdk文档当中对对象头的解释

object header Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format

上述引用中提到一个java对象头包含了2个word,并且好包含了堆对象的布局、类型、GC状态、同步状态和标识哈 希码,具体怎么包含的呢?又是哪两个word呢?

mark word The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

mark word为第一个word根据文档可以知他里面包含了锁的信息,hashcode,gc信息等等,第二个word是什么 呢?

klass pointer The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the “klass” contains a C++ style “vtable”.

klass word为对象头的第二个word主要指向对象的元数据
在这里插入图片描述

假设我们理解一个对象头主要上图两部分组成(数组对象除外,数组对象的对象头还包含一个数组长度),那么 一个java的对象头多大呢?我们从JVM的源码注释中得知到一个mark word一个是64bit,那么klass的长度是多少呢?
所以我们需要想办法来获得java对象头的详细信息,验证一下他的大小,验证一下里面包含的信息是否正确。
根据上述利用JOL打印的对象头信息可以知道一个对象头是12Byte,其中8Byte是mark word 那么剩下的4Byte就是klass word了,和锁相关的就是mark word了,那么接下来重点分析mark word里面信息
在无锁的情况下markword当中的前56bit存的是对象的hashcode,那么来验证一下

java代码和运行结果:

public class JOLExample2 {
    public static void main(String[] args) throws Exception {
        A a= new A();
        out.println("befor hash");
        //没有计算HASHCODE之前的对象头
        out.println(ClassLayout.parseInstance(a).toPrintable());
        //JVM 计算的hashcode
        out.println("jvm------------0x"+Integer.toHexString(a.hashCode()));
        HashUtil.countHash(a);
        //当计算完hashcode之后,我们可以查看对象头的信息变化
        out.println("after hash");
        out.println(ClassLayout.parseInstance(a).toPrintable());

    }
}
import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class HashUtil {
    public static void countHash(Object object) throws NoSuchFieldException, IllegalAccessException {
        // 手动计算HashCode
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        long hashCode = 0;
        for (long index = 7; index > 0; index--) {
            // 取Mark Word中的每一个Byte进行计算
            hashCode |= (unsafe.getByte(object, index) & 0xFF) << ((index - 1) * 8);
        }
        String code = Long.toHexString(hashCode);
        System.out.println("util-----------0x"+code);

    }
}

分析结果3:
1-----9行是没有进行hashcode之前的对象头信息,可以看到1-7B的56bit没有值,打印完hashcode之后16----21行就有值了,为什 么是1-7B,不是0-6B呢?因为是小端存储。其中12行是我们通过hashcode方法打印的结果,13行是我根据1-7B的信息计算出来的 hashcode,所以可以确定java对象头当中的mark work里面的后七个字节存储的是hashcode信息,那么第一个字节当中的八位分别存的
就是分带年龄、偏向锁信息,和对象状态,这个8bit分别表示的信息如下图(其实上图也有信息),这个图会随着对象状态改变而改变, 下图是无锁状态下
java 对象头信息 (二)_第1张图片
关于对象状态一共分为五种状态,分别是无锁、偏向锁、轻量锁、重量锁、GC标记,那么2bit,如何能表示五种状 态(2bit多只能表示4中状态分别是:00,01,10,11),jvm做的比较好的是把偏向锁和无锁状态表示为同一个状态,然 后根据图中偏向锁的标识再去标识是无锁还是偏向锁状态。什么意思呢?写个代码分析一下,在写代码之前我们先记得 无锁状态下的信息:00000001,然后写一个偏向锁的例子看看结果

你可能感兴趣的:(JAVA)