Java对象在内存布局

2021/10/28 7:14:45

本文主要是介绍Java对象在内存布局,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一. 对象内存构成

对象的组成组成

JVM 中,Java对象保存在堆中时,由以下三部分组成:

  • 对象头(object header):包括了关于堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息(12byte)。

           对象头由三部分组成:

   1,Mark Word

           2,指向类的指针

           3,数组长度(只有数组对象才有)

  • 实例数据(Instance Data):主要是存放类的数据信息,父类的信息,对象字段属性信息。
  • 对齐填充(Padding):为了字节对齐,填充的数据,不是必须的(对象的大小是2^3的整数倍,时间换空间)。

 Object o = new Object();在内存中占多大内存?

在开启压缩的情况下,对象头(object header)占据12byte(96bit),其中 mark word占8byte(64bit),klass pointe 占4byte,另外剩余4byte是填充对齐的。对象的引用在栈内存占4byte,总共是16+4=20byte

对象

 

Mark Word在64位JVM中的存储

锁升级

  1. 当没有被使用时,这就是一个普通的对象,Mark Word记录对象的HashCode(调用HashCode方法时会写入,位调用之前为空),锁标志位是01,是否偏向锁那一位是0。
  2. 当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前54bit记录抢到锁的线程id,表示进入偏向锁状态。
  3. 当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。
  4. 当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。
  5. 线程B在偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。
  6. 轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。
  7. 自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

锁降级:只会发生在GC的额时候,这时候只有GC线程使用,没有讨论的意义

锁消除:只有一个线程在使用某个资源,JVM会消除这个资源的锁

锁粗化

while(i<1000){

a.show();

}

这时候JVM会将a的锁加到while上面



这篇关于Java对象在内存布局的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程