zoukankan      html  css  js  c++  java
  • & 并发编程-3-对象头

    toc

    并发编程之synchronize原理-对象头

    扫盲

    存储单位的bit 和 Byte
    1.bit(比特)
    bit也就是我们不一定听说过的比特,大名鼎鼎的比特币就是以此命名的。它的简写为小写字母 “b” 。
    作为信息技术的最基本存储单元,因为比特实在太小了,所以大家生活中并不是经常听到。那么 bit 是什么呢?
    电脑是以二进制存储以及发送接收数据的。二进制的一位,就叫做 1 bit。也就是说 bit 的含义就是二进制数中的一个数位,即 “0” 或者 "1"。

    2.Byte(字节)
    Byte 是字节的英文写法。它的简写为大写字母 “B"。
    既然名字叫字节,那肯定跟字符有关系。是的。英文字符通常是一个字节,也就是 1B,中文字符通常是两个字节,也就是 2B。
    字节 Byte 和比特 bit 的换算关系是 1 Byte = 8 bit 。

    1byte = 8bit
    8byte = 64bit
    B=byte
    b=bit

    自己实现一个锁

    java对象头分析

    所谓锁就是给对象一个标识;而这个标识就是存在对象头当中
    这里截取一张hotspot的源码当中的注释

    image-20210311102833000

    这张图换成人可读的表格如下 此图极其重要

    image-20210311102847892

    java的对象头在对象的不同状态下会有不同的表现形式,主要有三种状态:无锁状态、加锁状态、GC状态。那么我可以理解java当中的加锁其实可以理解是给对象上锁,也就是改变对象头的状态,如果上锁成功则进入同步代码块。
    但是java当中的锁又分为很多种,从上图可以看出大体分为偏向锁、轻量锁、重量锁三种锁状态。这三种锁的效率完全不同(依次降低)

    java对象的布局以及对象头

    1、利用JOL来分析java的对象布局

    首先maven添加JOL的依赖

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

    A.java 源码

    package com.enjoy.entity;
    public class A {
    }

    JOLExample1.java

    package com.enjoy.test;
    import com.enjoy.entity.A;
    import org.openjdk.jol.info.ClassLayout;
    import org.openjdk.jol.vm.VM;
    
    public class Test1 {
      static A a= new A();
      public static void main(String[] args) {
        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(A.class).toPrintable());
        System.out.println(ClassLayout.parseInstance(a).toPrintable());
     }
    }

    运行结果:

    image-20210311102902351

    分析结果1
    整个对象一共16B,其中对象头(Object header)12B,还有4B是对齐的字节(因为在64位虚拟机上对象的大小必须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据是0B;

    1. 什么叫对象的实例数据?
    2. 对象头里面的12B到底存的是什么?
      首先要明白什么是对象的实例数据很简单:我们可以在A当中添加一个boolean的字段(boolean占1B),然后看结果:

    A.java

    package com.enjoy.entity;
    public class A {
      boolean f;
    }

    运行结果2

    image-20210311102916503

    分析结果2
    整个对象的大小还是没有改变一共16B(B=byte),其中对象头(Object header)12B,boolean字段f(对象的实例数据)占1B、剩下的3B就是对齐字节。由此我们可以认为一个对象的布局大体分为三个部分分别是对象头(Object header)、对象的实例数据字节对齐;

    对象布局:

    image-20210311102932268

    接下来讨论第二个问题,对象头为什么是12B?这个12B当中分别存储的是什么呢?(不同位数的VM对象头的长度不一样,这里指的是64bit的vm)

    2、大小端模式

    再接着往下研究对象的时候先要高明白大小端存储,一般家用笔记本都是小端模式,那么什么是小端模式呢?

    小端模式:高字节存在高地址,低字节存在低地址。

    image-20210311103032345

    3、对象头的规范

    http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html

    上述链接文档部分截图:

    image-20210311103111832

    image-20210311103130367

    首先引用openjdk文档当中对 对象头的解释
    上述引用中提到了一个java对象头包含了2个word,并且还包含了堆对象布局、类型、GC状态、同步状态和标识哈希码,具体怎么包含的呢?又是哪两个word?

    对象头的结构大概如下图

    image-20210311103151035

    mark word为第一个word根据文档可以知他里面包含了锁的信息,hashcode,gc信息等等
    klass word(klass pointer)为对象头的第二个word主要指向对象的元数据,他是一个指针;

    假设我们理解一个对象头主要上图两部分组成(数组对象除外,数组对象的对象头还包含一个数组长度),那么一个java的对象头多大呢?我们从JVM的源码注释中得知到一个mark word一个是8字节, 64位,那么klass的长度是多少呢?(12-8=4byte)(对象头(Object header)一共12B)
    所以我们需要想办法来获得java对象头的详细信息,验证一下他的大小,验证一下里面包含的信息是否正确。

    java代码 Test1

    public class Test1 {
      static A a= new A();
      //-XX:BiasedLockingStartupDelay=0
      public static void main(String[] args) throws InterruptedException {
        log.debug("----hashcode before");
        log.debug(ClassLayout.parseInstance(a).toPrintable());
        //转化成16进制,方便比较
        log.debug(Integer.toHexString(a.hashCode()));
        log.debug("----hashcode after");
        //计算完hashcode之后的a对象的布局
        log.debug(ClassLayout.parseInstance(a).toPrintable());
     }
    }

    运行结果

    image-20210311103255722

    http://tool.oschina.net/hexconvert/ 进制转换

    分析结果3:

    没有进行hashcode之前的对象头信息,可以看到2b-8b之前的的56bit是没有值,打印完hashcode之后行就有值了,为什么是2-8B,不应该是1-7B呢?因为是小端存储。那么第一个字节当中的八位分别存的就是分带年龄、偏向锁信息,和对象状态,这个8bit分别表示的信息如下图;这个图会随着对象状态改变而改变,下图是无锁状态下

    image-20210311103309408

    其中的是否可偏向标识在无锁情况下会根据是否计算hashcode而变化;
    因为如果计算了hashcode之后对象便变得不可偏向;为什么?

    关于对象状态一共分为五种状态,分别是无锁不可偏向、无锁可向锁、轻量锁、重量锁、GC标记,那么2bit,如何能表示五种状态(2bit最多只能表示4中状态分别是:00,01,10,11),jvm做的比较好的是把是否可偏向表示为一个状态,然后根据图中偏向锁的标识再去标识是可偏向的还是不可偏向的

    00 轻量锁
    01 无锁/偏向锁
    10 重量锁
    11 GC标记

    关于对象头目前只要了解这些就够了,下节课来解释什么是偏向锁,工作原理是什么和轻量锁的性能对比

    这节课只要知道对象头里面存了什么东西就可以了;
    关于课上做的那个锁的膨胀的实验大家可以自己用JOL去看看,锁的膨胀非常复杂,我只是先大概的给大家演示了一下膨胀过程过程具体大概如下图(切记这个图不能作为面试,只是一个过渡结果,实际锁的膨胀远比这个复杂的多了多)

    image-20210311102507109

  • 相关阅读:
    Java Mail学习整理
    @RequestBody, @ResponseBody 注解详解(转)
    @RequestMapping 用法详解之地址映射(转)
    理解static关键字
    ClassLoader如何加载class
    请说出作用域public,private,protected,以及不写时的区别
    分页语句-取出sql表中第31到40的记录(以自动增长ID为主键)
    Java构造器和方法的区别
    8月收到的最新更新附近的人交友系统源码,自动打招呼,自动发视频通话+自动聊天多功能机器人交友源码
    8月新亲测完美短视频点赞系统支持抖音+快手+刷宝+微视等所有主流短视频点赞/关注/评论系统源码
  • 原文地址:https://www.cnblogs.com/doagain/p/15018627.html
Copyright © 2011-2022 走看看