JVM之对象的内存布局

1.对象内存划分:

在Hotspot虚拟机中,对象的内存布局可以划分为三个部分:

1. 对象头
2. 实例数据
3. 对齐填充

2.对象头

对象头中储存了两类信息:

  1. 对象自身运行时的数据,官方称为“Mark Word”,包括哈希码、GC分代年龄、锁状态标志、线程持有的锁等等。在32位和64位虚拟机中,Mark Word占据的内存空间分别为32个bit和64个bit,即4个字节和8个字节。
  2. 类型指针,官方称为“Klass Pointer”,即对象指向它的类型元数据的指针。在虚拟机默认开启指针压缩的情况下,Klass Pointer占据4byte,如果使用-XX:-UseCompressedOops关闭指针压缩,Klass Pointer占据8byte

对于32位虚拟机,25个比特用来储存对象哈希码,4个比特用于储存对象分代年龄,2个比特用于储存锁标志位,1个比特固定为0。查看JDK源码markOop.cpp文件:

//  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位虚拟机,25个比特为空,31个比特用来储存对象哈希码,1个比特为空,4个比特用于储存对象分代年龄,1个比特用于储存偏向锁信息,2个比特储存锁标志位。

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

储存对象分代年龄的空间为4个比特,也可以解释为什么虚拟机垃圾回收机制中默认熬过15次GC的新生代对象会进入老年代。

对象按锁状态可以分为五类:

  1. 未锁定
  2. 偏向锁锁定
  3. 轻量级锁定
  4. 重量级锁定
  5. GC标记

64位虚拟机中用1个比特用于储存偏向锁信息,2个比特储存锁标志位,共计3个比特用于表示对象的5种锁状态。

3.实例数据

实例数据部分是对象真正储存的有效信息,即我们在程序代码中所定义的字段内容。这一部分的内存大小按照字段多少来确定,是不固定的,如果对象中不包含任何字段内容,则为空。

4.对齐填充

这一部分和实例数据部分一样,也不是必须存在的,它没有特殊的含义,只是起到占位符的作用。在Hotspot虚拟机中规定,任何对象的大小都必须是8个字节的整数倍,如果对象头加上实例数据的大小不足8的整数倍,则通过对齐填充来进行补全。

5.通过JOL查看对象内存结构

jol是一种可以查看对象结构的jar包,其maven依赖如下:

<dependency>
	<groupId>org.openjdk.jol</groupId>
	<artifactId>jol-core</artifactId>
	<version>0.9</version>
</dependency>

测试代码如下:

public class Head {
    boolean flag = true;

    public static void main(String[] args) {
        Head head = new Head();
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}

这里需要注意的是,如果不对head对象进行hashcode计算,得到的对象信息里储存hashcode的比特为都为0:

 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)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     1   boolean Head.flag                                 true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

所以先进行hashcode计算,并转化为16进制

public class Head {
    boolean flag = true;

    public static void main(String[] args) {
        Head head = new Head();
        System.out.println(Integer.toHexString(head.hashCode()));
        System.out.println(ClassLayout.parseInstance(head).toPrintable());
    }
}

结果:

330bedb4
com.kuangshen.Object_head.Head object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 b4 ed 0b (00000001 10110100 11101101 00001011) (200127489)
      4     4           (object header)                           33 00 00 00 (00110011 00000000 00000000 00000000) (51)
      8     4           (object header)                           05 c1 00 20 (00000101 11000001 00000000 00100000) (536920325)
     12     1   boolean Head.flag                                 true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

这里又涉及一个小端储存和大端储存的问题:

  • 小端储存:数据的高位字节存放在地址的高端 低位字节存放在地址低端
  • 大端储存:数据的高位字节存放在地址的低端 低位字节存放在地址高端

因为采用的小端储存,所以对象头信息储存顺序和markOop.cpp中所说明的顺序相反,所以按大端储存,对象头信息为:

00000000 00000000 00000000 00110011 00001011 11101101 10110100 00000001

各部分信息都能对应上。

参考资料:
1.《深入理解Java虚拟机》第三版 周志明

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值