zoukankan      html  css  js  c++  java
  • 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对象头与synchronized锁的升级过程 - 七月流星丶 - 博客园 (cnblogs.com)

  • 相关阅读:
    P4999 烦人的数学作业
    P3413 SAC#1
    P2657 [SCOI2009]windy数
    P2602 [ZJOI2010]数字计数
    JSOI2007 建筑抢修
    CF161B Discounts
    Description
    Street Numbers
    Pizza Cutting
    Supermean
  • 原文地址:https://www.cnblogs.com/isalo/p/15469194.html
Copyright © 2011-2022 走看看