前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >打印Java对象头

打印Java对象头

作者头像
@阿诚
发布2021-02-04 11:28:33
2.4K0
发布2021-02-04 11:28:33
举报
文章被收录于专栏:Panda诚Panda诚

对象头形式

JVM中对象头的方式有以下两种(以32位JVM为例)

普通对象

代码语言:javascript
复制
|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    Klass Word (32 bits) |
|------------------------------------|-------------------------|

数组对象

代码语言:javascript
复制
|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|

对象头的组成

Mark Word

这部分主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等。mark word的位长度为JVM的一个Word大小,也就是说32位JVM的Mark word为32位,64位JVM为64位。 为了让一个字大小存储更多的信息,JVM将字的最低两个位设置为标记位,不同标记位下的Mark Word示意如下:

代码语言:javascript
复制
|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
|-------------------------------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
|-------------------------------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
|-------------------------------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                              | lock:2 |    Marked for GC   |
|-------------------------------------------------------|--------------------|
代码语言:javascript
复制
|--------------------------------|-----------------------|------------------------|
|           biased_lock          |         lock          |         state          |
|--------------------------------|-----------------------|------------------------|
|                0               |          01           |          无锁           |
|--------------------------------|-----------------------|------------------------|
|                1               |          01           |          偏向锁         |
|--------------------------------|-----------------------|------------------------|
|                                |          00           |         轻量级锁        |
|--------------------------------|-----------------------|------------------------|
|                                |          10           |         重量级锁        |
|--------------------------------|-----------------------|------------------------|
|                                |          11           |           GC           |
|--------------------------------|-----------------------|------------------------|
  • identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。
  • age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。
  • biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
  • lock: 锁状态标记位,该标记的值不同,整个mark word表示的含义不同。
  • thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。
  • epoch:偏向时间戳。
  • ptr_to_lock_record:指向栈中锁记录的指针。
  • ptr_to_heavyweight_monitor:指向线程Monitor的指针。

class pointer

这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32位的JVM为32位,64位的JVM为64位。 如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存可以使用选项+UseCompressedOops开启指针压缩,其中,oop即ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位:

  1. 每个Class的属性指针(即静态变量)
  2. 每个对象的属性指针(即对象变量)
  3. 普通对象数组的每个元素指针

当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

array length

如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。64位JVM如果开启+UseCompressedOops选项,该区域长度也将由64位压缩至32位。

打印对象头

注:以下测试我本机是64位,无锁:unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2;偏向锁:thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2

引入打印工具

代码语言:javascript
复制
 <dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.8</version>
    <scope>test</scope>
</dependency>

代码:

代码语言:javascript
复制
static class A {
    boolean flag = false;
}

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

输出结果:

代码语言:javascript
复制
...A 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)                           93 c2 00 f8 (10010011 11000010 00000000 11111000) (-134167917)
     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

最后两位是lock01,表示无锁或者是偏向锁。则倒数第三位是biased_lock0,标识当前对象无锁。

代码语言:javascript
复制
00000    0    01

JVM启动时会进行一系列的复杂活动,比如装载配置,系统类初始化等等。在这个过程中会使用大量synchronized关键字对对象加锁,且这些锁大多数都不是偏向锁。为了减少初始化时间,JVM默认延时加载偏向锁。这个延时的时间大概为4s左右,具体时间因机器而异。当然我们也可以设置JVM参数 -XX:BiasedLockingStartupDelay=0 来取消延时加载偏向锁。

修改代码:

代码语言:javascript
复制
public static void main(String[] args) throws InterruptedException {
    Thread.sleep(5000);
    A a = new A();
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

输出结果:

代码语言:javascript
复制
...A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           93 c2 00 f8 (10010011 11000010 00000000 11111000) (-134167917)
     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

最后两位是lock01,表示无锁或者是偏向锁。则倒数第三位是biased_lock1,标识当前对象可偏向(Thread的值还是0)。

代码语言:javascript
复制
00000    0    01

前面说过,identity_hashcode对象标识Hash码,采用延迟加载技术。 修改代码

代码语言:javascript
复制
public static void main(String[] args) throws InterruptedException {
    Thread.sleep(5000);
    A a = new A();
    System.out.println(System.identityHashCode(a));
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

输出结果:

代码语言:javascript
复制
777874839
A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 97 6d 5d (00000001 10010111 01101101 01011101) (1567463169)
      4     4           (object header)                           2e 00 00 00 (00101110 00000000 00000000 00000000) (46)
      8     4           (object header)                           93 c2 00 f8 (10010011 11000010 00000000 11111000) (-134167917)
     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

00000001标识无锁状态 ,777874839十进制转二进制结果是2e 5d 6d 97

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Panda诚 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 对象头形式
    • 普通对象
      • 数组对象
      • 对象头的组成
        • Mark Word
          • class pointer
            • array length
            • 打印对象头
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档