美文网首页
Java对象头及锁

Java对象头及锁

作者: 晚歌歌 | 来源:发表于2021-08-18 21:38 被阅读0次

对象组成

  • 对象头
  • 实例数据:对象的实例数据就是在java代码中能看到的属性和他们的值
  • 对齐填充字节:因为JVM要求java的对象占的内存大小应该是8字节的倍数,所以后面有几个字节用于把对象的大小补齐至8字节的倍数,没有特别的功能


    image.png

对象头

Java对象头由以下三部分组成

  • Mark Word
  • 指向类的指针
  • 数组长度(只有数组对象才有)

Mark Word

Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。
Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit
Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:


image.png

其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。
JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。

可以看出无锁情况下,前面位数保存的是对象的HashCode,那么偏向锁、轻量级锁、重量级锁如何获取对象的HashCode?

HashCode生成规则:在无锁状态下,Mark Word 中可以存储对象的 identity hash code 值。当对象的 hashCode() 方法(非用户自定义)第一次被调用时,JVM 会生成对应的 identity hash code 值,并将该值存储到 Mark Word 中。后续如果该对象的hashCode() 方法再次被调用则不会再通过 JVM 进行计算得到,而是直接从 Mark Word 中获取。只有这样才能保证多次获取到的 identity hash code 的值是相同的

  • 对于轻量级锁,获取锁的线程栈帧中有锁记录(Lock Record)空间,用于存储 Mark Word 的拷贝,官方称之为 Displaced Mark Word,该拷贝中可以包含 identity hash code,所以轻量级锁可以和 identity hash code 共存

  • 对于重量级锁,ObjectMonitor 类里有字段可以记录非加锁状态下的 mark word,其中也可以存储 identity hash code 的值,所以重量级锁也可以和 identity hash code 共存。

  • 对于偏向锁,在线程获取偏向锁时,会用 Thread ID 和 epoch 值覆盖 identity hash code 所在的位置。如果一个对象的hashCode() 方法已经被调用过一次之后,这个对象还能被设置偏向锁么?答案是不能。因为如果可以的,那 Mark Word中 的 identity hash code 必然会被偏向线程Id给覆盖,这就会造成同一个对象前后两次调用 hashCode() 方法得到的结果不一致。

HotSpot VM 的锁实现机制是:

  • 当一个对象已经调用默认 hashCode() 或者 System.identityHashCode(),即计算过 identity hash code 后,它就无法进入偏向锁状态。这意味着,如果要在不发生争用的对象上进行同步,则最好覆盖默认hashCode()实现,否则JVM不会优化。
  • 当一个对象当前正处于偏向锁状态,并且需要计算其 identity hash code 的话,则它的偏向锁会被撤销,并且锁会膨胀为轻量级锁或者重量锁;
  • 轻量级锁的实现中,会通过线程栈帧的锁记录存储 Displaced Mark Word;
  • 重量锁的实现中,ObjectMonitor 类里有字段可以记录非加锁状态下的 mark word,其中可以存储 identity hash code 的值

指向类的指针

该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
Java对象的类数据保存在方法区。

数组长度

只有数组对象保存了这部分数据。
该数据在32位和64位JVM中长度都是32bit。

压缩指针

-XX:+UseCompressedOops,64位JVM才有且1.6之后默认开启,作用是将对象头中的类指针压缩至32位

原理

对于32位的CPU和操作系统来说,32位的指针可以表示
2的32次方 = 4Gbit 个地址,而每个地址占用的内存空间为1个byte(因为CPU运算位数是32位,所以每次都是按byte读取),因此32操作系统支持的最大内存位4Gbit * 8 = 4GB

对于64位的JVM来说,64位的指针理论上可以表示的地址无限大
指针压缩后变为32位指针,如上所述理论上只能表示 4Gbit 个地址,支持4GB的内存,但是假如每个地址占用的内存空间改为8个byte,那么就能够支持 4GB * 8 = 32GB的内存了,JVM就是这样运用8字节对齐填充的特性通过映射的方式达到内存扩充的效果

如果堆大小大于 32 GB,压缩指针就不能用了

相关文章

网友评论

      本文标题:Java对象头及锁

      本文链接:https://www.haomeiwen.com/subject/rnjnbltx.html