美文网首页
java对象的内存布局和锁

java对象的内存布局和锁

作者: Bfmall | 来源:发表于2023-06-04 14:36 被阅读0次

    一、对象在内存中的存储布局

    对象的内存布局分为两种,普通对象和数组对象


    image.png image.png

    1、对象头-Mark Word

    用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持久的锁、偏向线程的ID等
    通过存储的内容得知对象头是锁机制和GC的重要基础。

    image.png

    以 32位虚拟机为例,来看一下其 Mark Word 的字节具体是如何分配的?

    无锁 :
    对象的hashcode:25bit ; 存放对象分代年龄:4bit; 存放是否偏向锁的标识位:1bit; 存放锁标识位为01:2bit

    偏向锁:
    在偏向锁中划分更细。
    开辟 25bit 的空间,其中存放线程ID:23bit; 存放Epoch:2bit;存放对象分代年龄:4bit ;存放是否偏向锁标识,:1bit (0表示无锁,1表示偏向锁);锁的标识位还是01

    轻量级锁:
    在轻量级锁中直接开辟 30bit 的空间存放指向栈中锁记录的指针,2bit 存放锁的标志位,其标志位为00

    重量级锁:
    在重量级锁中和轻量级锁一样,30bit 的空间用来存放指向重量级锁的指针,2bit 存放锁的标识位,为11

    GC标记:
    开辟30bit 的内存空间却没有占用,2bit 空间存放锁标志位为11。

    其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态

    2、类型指针-Class Pointer
    Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

    3、对象:实例数据;数组:数组长度+数组数据

    如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据。
    数组长度:存储数组长度,固定占用4个字节
    实例数据、数组数据:存储数据

    不同的类型所占的空间不同:


    image.png

    4、对齐填充字节- padding :
    用于补齐对象内存长度的。
    因为JVM要求java代码的对象必须是8bit的倍数。如果一个对象用不到8N个字节则需要对其填充,以此来补齐对象头和实例数据占用内存之后剩余的空间大小。如果对象头和实例数据已经占满了JVM所分配的内存空间,那么就不用再进行对齐填充了。所有的对象分配的字节总SIZE需要是8的倍数,如果前面的对象头和实例数据占用的总SIZE不满足要求,则通过对齐数据来填满。

    二、工具
    1、jol工具
    Idea 导入 pom依赖

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

    jol常用方法:

        1.使用jol计算对象的大小(单位为字节):ClassLayout.parseInstance(obj).instanceSize()
        2.使用jol查看对象内部的内存布局:ClassLayout.parseInstance(obj).toPrintable()
        3.查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()
    
    

    2、进制工具

    https://hex.buyaocha.com/

    三、实验

    1、对象 和 数组

    (1)对象类型

    代码:

    public class markword01_EmptyProperties {
        public static void main(String[] args) {
            System.out.println("空属性-对象布局================================");
            markword01_EmptyProperties entity = new markword01_EmptyProperties();
            // 打印java 对象内存布局
            System.out.println(ClassLayout.parseInstance(entity).toPrintable());
        }
    }
    

    输出结果:

    com.tsj.threads.markword01_EmptyProperties object internals:
    OFF  SZ   TYPE DESCRIPTION               VALUE
      0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4        (object header: class)    0xf800c105
     12   4        (object alignment gap)    
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

     - OFF(OFFSET):偏移地址,单位字节; 
     - SZ(SIZE):占用的内存大小,单位为字节; 
     - TYPE DESCRIPTION:类型描述。
            object header为对象头,object header 包含 mark + class;
            gap字段,表示补齐padding。 
    - VALUE:对应内存中当前存储的值; 
    - Instance size:实例字节数值大小,
            即:一个空的java对象(不包含任意字段属性)实例,其实例大小为16Byte。
            怎么计算?mark + class + gap = 16. 
    -  Space losses: 0 bytes internal + 4 bytes external
       = 4 bytes total:表示内存补齐Padding,占用4个字节
    

    (2)数组

    代码:

    public class markword01_EmptyArrayEntity {
        public static void main(String[] args) {
            System.out.println("数组布局================================");
            markword01_EmptyArrayEntity[] entity = new markword01_EmptyArrayEntity[4];
            // 打印java 对象内存布局
            System.out.println(ClassLayout.parseInstance(entity).toPrintable());
        }
    }
    

    输出结果:

    [Lcom.tsj.threads.markword01_EmptyArrayEntity; object internals:
    OFF  SZ                                          TYPE DESCRIPTION                               VALUE
      0   8                                               (object header: mark)                     0x0000000000000001 (non-biasable; age: 0)
      8   4                                               (object header: class)                    0xf800c143
     12   4                                               (array length)                            4
     12   4                                               (alignment/padding gap)                   
     16  16   com.tsj.threads.markword01_EmptyArrayEntity markword01_EmptyArrayEntity;.<elements>   N/A
    Instance size: 32 bytes
    Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
    

    结果分析:

     - OFF:偏移地址,单位字节; 
     - SZ:占用的内存大小,单位为字节; 
     - TYPE DESCRIPTION:类型描述。
           bject header为对象头,object header 包含 mark + class;
           gap字段,表示补齐padding。
           array length:表示数组长度 
    - VALUE:对应内存中当前存储的值; 
    - Instance size:实例字节数值大小,
           即:一个空的java数组,其实例大小为32Byte。
           怎么计算?mark + class +  array length + gap = 32. 
    -  Space losses: 0 bytes internal + 4 bytes external
       = 4 bytes total:表示内存补齐Padding,占用4个字节
    

    2、padding对齐 vs 压缩验证

    (1) 创建三个不同属性的类

    Student:

    public class Student {
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
    

    StudentV1:

    public class StudentV1 {
        private String nameV1;
        private Integer ageV1;
        private int sexV1;
    
        public String getNameV1() {
            return nameV1;
        }
    
        public void setNameV1(String nameV1) {
            this.nameV1 = nameV1;
        }
    
        public Integer getAgeV1() {
            return ageV1;
        }
    
        public void setAgeV1(Integer ageV1) {
            this.ageV1 = ageV1;
        }
    
        public int getSexV1() {
            return sexV1;
        }
    
        public void setSexV1(int sexV1) {
            this.sexV1 = sexV1;
        }
    

    StudentV2:

    public class StudentV2 {
        private String nameV1;
        private Integer ageV1;
        private int sexV1;
    
        private boolean live;
    
        public String getNameV1() {
            return nameV1;
        }
    
        public void setNameV1(String nameV1) {
            this.nameV1 = nameV1;
        }
    
        public Integer getAgeV1() {
            return ageV1;
        }
    
        public void setAgeV1(Integer ageV1) {
            this.ageV1 = ageV1;
        }
    
        public int getSexV1() {
            return sexV1;
        }
    
        public void setSexV1(int sexV1) {
            this.sexV1 = sexV1;
        }
    
        public boolean isLive() {
            return live;
        }
    
        public void setLive(boolean live) {
            this.live = live;
        }
    }
    

    (2) ClassLayout执行输出内存布局:

     public static void main(String[] args) {
            System.out.println("有属性-对象布局 1================================");
            Student student = new Student();
            System.out.println(ClassLayout.parseInstance(student).toPrintable());
    
            System.out.println("有属性-对象布局 2================================");
            StudentV1 studentV1 = new StudentV1();
            System.out.println(ClassLayout.parseInstance(studentV1).toPrintable());
    
    
            System.out.println("有属性-对象布局 3================================");
            StudentV2 studentV2 = new StudentV2();
            System.out.println(ClassLayout.parseInstance(studentV2).toPrintable());
    
        }
    

    (3) 输出结果:

    有属性-对象布局 1================================
    # WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    有属性-对象布局 2================================
    com.tsj.threads.StudentV1 object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800f359
     12   4                 int StudentV1.sexV1           0
     16   4    java.lang.String StudentV1.nameV1          null
     20   4   java.lang.Integer StudentV1.ageV1           null
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    有属性-对象布局 3================================
    com.tsj.threads.StudentV2 object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800f39e
     12   4                 int StudentV2.sexV1           0
     16   1             boolean StudentV2.live            false
     17   3                     (alignment/padding gap)   
     20   4    java.lang.String StudentV2.nameV1          null
     24   4   java.lang.Integer StudentV2.ageV1           null
     28   4                     (object alignment gap)    
    Instance size: 32 bytes
    Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
    

    (4) 结果分析:

    - Student:
        Instance size: 24 bytes;
        24 bytes = mark(8) + class(4) + name(4,压缩) + age(4,压缩) + gap(4)
        name 类型:String,age类型:Integer,都属于引用类型。JDK默认开启了压缩,8位压缩至4位。
        进行了一次补齐,补齐4bytes
    - StudentV1:
        Instance size: 24 bytes
        24 bytes = mark(8) + class(4) + sexV1(4) +  nameV1(4,压缩) + ageV1(4,压缩) 
        nameV1 类型:String,ageV1类型:Integer,都属于引用类型。JDK默认开启了压缩,8位压缩至4位。另外,sexV1类型为int,基本类型无法压缩。
        没有进行补齐
    - StudentV2:
        Instance size: 32 bytes
        24 bytes = mark(8) + class(4) + sexV1(4) + live (1) + gap(3) + nameV1(4,压缩) + ageV1(4,压缩) + gap(4)
        进行了两次补齐,一次为internal,为live补齐了3bities,一次为external,为整个对象补齐4字节。
    

    3、锁对象的内存布局

    主要是针对synchronized,JDK 1.6 优化了synchronized,提出了锁升级的机制,即无锁-偏向锁-轻量级锁-重量级锁,我这里不验证锁升级的过程,主要验证的数这四种锁在对象头是怎么存储和标识的。

    锁类型(按锁的状态分类)

    non-biasable 无锁且不可偏向
    biasable 无锁可偏向
    biased 偏向锁
    thin lock 轻量级锁
    fat lock 重量级锁
    

    |--------------------------------------------------------------------------------------------------------------|
    | Object Header (128 bits) |
    |--------------------------------------------------------------------------------------------------------------|
    | Mark Word (64 bits) | Klass Word (64 bits) |
    |--------------------------------------------------------------------------------------------------------------|
    | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 无锁
    |----------------------------------------------------------------------|--------|------------------------------|
    | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 偏向锁
    |----------------------------------------------------------------------|--------|------------------------------|
    | ptr_to_lock_record:62 | lock:2 | OOP to metadata object | 轻量锁
    |----------------------------------------------------------------------|--------|------------------------------|

    | ptr_to_heavyweight_monitor:62 | lock:2 | OOP to metadata object | 重量锁
    |----------------------------------------------------------------------|--------|------------------------------|
    | | lock:2 | OOP to metadata object | GC
    |--------------------------------------------------------------------------------------------------------------|
    ————————————————

    ock: 锁状态标记位,该标记的值不同,整个mark word表示的含义不同。

    biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。

    从分布可以得出,看锁标记,直接看后 3 位即可,如果给到你锁类型标识,你只需要看前后2位就行。

    biased_lock lock 16进制 状态
    0 01 1 无锁
    1 01 5 偏向
    0 00 0 轻量
    0 10 2 重量
    0 11 3 GC

    (1)无锁

    代码:

    public class SyncTest01 {
    
        public static void main(String[] args) throws InterruptedException {
            Student a = new Student();
            String str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println(str);//01    无锁状态
            new Thread() {
                public void run() {
                    //把a作为锁对象
                    synchronized (a) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
    
            Thread.sleep(10);
        }
    
    }
    

    输出结果:

    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

    主要锁的状态体现在第一行 object header: mark 的 value部分。
    mark标识:0x0000000000000001 (non-biasable; age: 0)
    non-biasable; age: 0
    (1)non-biasable:表示无锁且不可偏向
    (2)age:年龄:0
    0x0000000000000001
    对象头:0x0000000000000001,二进制表示:1
    

    (2)轻量级锁

    代码:

    public class SyncTest02 {
    
        public static void main(String[] args) throws InterruptedException {
            Student a = new Student();
    
            String str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println("未上锁,输出ClassLayout");
            System.out.println(str);//01    无锁状态
    
            new Thread(){
                public void run() {
                    //把a作为锁对象
                    synchronized(a) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
    
            Thread.sleep(10);
            System.out.println("上锁,输出ClassLayout");
            str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println(str);//00    轻量级锁
        }
    }
    

    输出结果:

    未上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000700004f9e9b0 (thin lock: 0x0000700004f9e9b0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

    上锁前:
        mark标识:0x0000000000000001 (non-biasable; age: 0)
        (1)non-biasable; age: 0
            non-biasable:表示无锁且不可偏向
        (2)0x0000000000000001,
            对象头:0x0000000000000001,二进制表示为:1。
        
    上锁后:
        mark标识:0x0000700004f9e9b0 (thin lock: 0x0000700004f9e9b0)
        (1)thin lock: 0x0000700004f9e9b0
            thin lock:表示轻量级锁;
            上锁的对象头:0x0000700004f9e9b0,二进制表示为:11100000000000000000100111110011110100110110000,看后两位表示:00,00:表示轻量级锁
        (2)0x0000700004f9e9b0
        对象头是0x0000700004f9e9b0,二进制表示为:11100000000000000000100111110011110100110110000
    
    加锁前对象处于无锁状态,加锁中处于轻量锁状态
    

    3)偏向锁
    同为轻量级锁代码(例子2的),另外还需要设置JVM参数。
    JDK默认开启偏向锁,但延时的,需要通过参数-XX:BiasedLockingStartupDelay=0禁用延时。(idea在VM Options设置就行了)
    偏向锁的开启和禁止延迟参数:

    -XX:BiasedLockingStartupDelay=0 -client -Xmx1024m -Xms1024m
    

    偏向锁的关闭参数

    -XX:+UseBiasedLocking -client -Xmx512m -Xms512m
    

    是否要在JVM中启用偏向锁,取决于业务方面
    如果加synchronized的方法在大部分时间内都只有一个线程来访问,其他很少的时间才会有多线程来并发访问,那么就有必要开启偏向锁。

    image.png

    输出结果:

    未上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000005 (biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x00007f9c7e0d3005 (biased: 0x0000001fe71f834c; epoch: 0; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

    上锁前:
        mark标识:0x0000000000000005 (biasable; age: 0)
        (1)non-biasable; age: 0
            non-biasable:表示无锁且不可偏向
            age:年龄:0
        (2)0x0000000000000005
            对象头是0x0000000000000005,二进制表示为:101。
            
    上锁后:
        mark标识:0x00007f9c7e0d3005 (biased: 0x0000001fe71f834c; epoch: 0; age: 0)
        (1)biased: 0x0000001fe71f834c; epoch: 0; age: 0
            biased:表示偏向锁
            上锁的对象:0x0000001fe71f834c,二进制表示为:1111111100111000111111000001101001100,看后三位表示:100,1:表示偏向锁,00:表示轻量级锁
            age:年龄:0
            如果开启了偏向锁,thread、epoch、age 都为 0
        (2)0x00007f9c7e0d3005
            对象头是:0x00007f9c7e0d3005,二进制表示为11111111001110001111110000011010011000000000101。
        
    加锁前对象处于无锁状态,加锁中处于偏向锁状态,在偏向锁中,对象头和加锁的对象头不是同一个。
    

    (4)重量锁(无锁 =》轻量级=》重量锁)

    代码:

    public class SyncTest03 {
        public static void main(String[] args) throws InterruptedException {
            Student a = new Student();
    
            String str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println("未上锁,输出ClassLayout");
            System.out.println(str);//01    无锁状态
    
            new Thread() {
                public void run() {
                    synchronized (a) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
    
            Thread.sleep(10);
            str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println("第一次上锁,输出ClassLayout");
            System.out.println(str);//00    轻量级锁
            new Thread() {
                public void run() {
                    synchronized (a) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
            Thread.sleep(10);
            str = ClassLayout.parseInstance(a).toPrintable();
            System.out.println("第二次上锁,输出ClassLayout");
            System.out.println(str);//10    重量级锁
        }
    }
    

    输出结果:

    未上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    第一次上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000700008ebc9b0 (thin lock: 0x0000700008ebc9b0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    第二次上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x00007f7bd001f13a (fat lock: 0x00007f7bd001f13a)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

    未上锁:
    mark标识:0x0000000000000001 (non-biasable; age: 0)
    (1)non-biasable; age: 0
        non-biasable:无锁
    
    第一次加锁,是真正加上锁
    mark标识:0x0000700008ebc9b0 (thin lock: 0x0000700008ebc9b0)
    (1)thin lock: 0x0000700008ebc9b0
        thin lock :轻量级锁
        0x0000700008ebc9b0,表示上锁的对象头,二进制为:11100000000000000001000111010111100100110110000,看后两位表示:00,00:表示轻量级锁
    (2)0x0000700008ebc9b0
        对象头是:0x0000700008ebc9b0
    
    第二次还是对同一把锁方法,存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。
    mark标识为:0x00007f7bd001f13a (fat lock: 0x00007f7bd001f13a)
    (1)fat lock: 0x00007f7bd001f13a
        fat lock :表示重量级锁
        0x00007f7bd001f13a,上锁的对象头,二进制为:11111110111101111010000000000011111000100111010,看后两位表示:01,01:表示轻量级锁
    (2)0x00007f7bd001f13a
        对象头是0x00007f7bd001f13a
    
    加锁前对象处于无锁状态,加锁中处于轻量锁状态,同一时间再次访问同一锁后,轻量级锁膨胀为重量级锁
    

    (4)无锁 =》偏向锁 =》重量锁
    代码和SyncTest03一致,在VM options禁止延迟偏向锁的设置

    -XX:BiasedLockingStartupDelay=0 -client -Xmx1024m -Xms1024m
    

    输出结果:

    未上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x0000000000000005 (biasable; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    第一次上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x00007fce8f96e005 (biased: 0x0000001ff3a3e5b8; epoch: 0; age: 0)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    第二次上锁,输出ClassLayout
    com.tsj.threads.Student object internals:
    OFF  SZ                TYPE DESCRIPTION               VALUE
      0   8                     (object header: mark)     0x00007fce9801128a (fat lock: 0x00007fce9801128a)
      8   4                     (object header: class)    0xf800c143
     12   4    java.lang.String Student.name              null
     16   4   java.lang.Integer Student.age               null
     20   4                     (object alignment gap)    
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    结果分析:

    上锁前
    mark标识:0x0000000000000005 (biasable; age: 0)
    (1)biasable; age: 0
        biasable:无锁可偏向
        age:年龄为0
    (2)0x0000000000000005
        当前对象头为0x0000000000000005,二级制为:101
    
    第一次上锁
    mark标识:0x00007fce8f96e005 (biased: 0x0000001ff3a3e5b8; epoch: 0; age: 0)
    (1)biased: 0x0000001ff3a3e5b8; epoch: 0; age: 0
        biased:表示偏向锁
        0x0000001ff3a3e5b8,二进制为:1111111110011101000111110010110111000 
    (2)0x00007fce8f96e005 
        当前对象头为0x00007fce8f96e005,二进制为:11111111100111010001111100101101110000000000101
    
    第二次上锁
    mark标识:0x00007fce9801128a (fat lock: 0x00007fce9801128a)
    (1)fat lock: 0x00007fce9801128a
        fat lock :表示重量级锁
        0x00007fce9801128a ,表示上锁对象头,二进制为:11111111100111010011000000000010001001010001010,查看后两位为10,10表示重量级锁
    (2)0x00007fce9801128a 
        表示当前对象头为0x00007fce9801128a,二进制为:11111111100111010011000000000010001001010001010
    
    加锁前对象处于无锁状态,加锁中处于偏向锁,同一时间再次访问同一锁后,偏向锁膨胀为重量级锁
    注意,在偏向锁状态,当前对象头和上锁的对象头往往不是同一个。
    

    ————————————————
    版权声明:本文为CSDN博主「童小绿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/tsj11514oo/article/details/127045106

    相关文章

      网友评论

          本文标题:java对象的内存布局和锁

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