前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >对象实例化与内存布局(深入)

对象实例化与内存布局(深入)

作者头像
逍遥壮士
发布2021-05-24 10:11:58
1.1K0
发布2021-05-24 10:11:58
举报
文章被收录于专栏:技术趋势技术趋势

上文:逃逸分析(Escape Analysis)技术


对象实例化

创建对象的方式

可以先参考原来的:HotSpot虚拟机对象如何被创建的?

创建对象方式有:new、Class的newInstance()、Constructor的newInst(Xxx)、使用clone()、使用反序列化、第三方库Objenesis;

new创建方式

代码语言:javascript
复制
Student student = new Student();

Class的newInstance()创建方式

代码语言:javascript
复制
Class clazz = Class.forName("com.hong.Student");
Student student = (Student) clazz.newInstance();

Constructor的newInst(Xxx)的创建方式

代码语言:javascript
复制
public class Student{
    private Integer age;
    private String name;
    public Student(Integer age,String name){
        this.age = age;
        this.name =name;
    }
}

使用clone()创建方式

代码语言:javascript
复制
Student clone = student.clone();

使用反序列化

代码语言:javascript
复制
public class Student implements Serializable {
    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;
    }

    public void say() {
        System.out.println("hello i'm hong!"+this.age);
    }


    public static void main(String[] args) throws Exception {

        Student student = new Student();
        student.setAge(10);
        String filePath = "com";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(student);
        oos.close();
        System.out.println("序列化完成!");

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Student student1 = (Student) ois.readObject();
        ois.close();
        student1.say();
        System.out.println("反序列化完成!");

    }
}

第三方库Objenesis创建对象

代码语言:javascript
复制
<dependency>
      <groupId>org.objenesis</groupId>
      <artifactId>objenesis</artifactId>
      <version>3.0.1</version>
</dependency>
代码语言:javascript
复制
Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator<Student> instantiator = objenesis.getInstantiatorOf(Student.class);
Student st1 = instantiator.newInstance();
st1.say();
System.out.println(st1.toString());
Student st2 = instantiator.newInstance();
st2.say();
System.out.println(st2.toString());

结果

代码语言:javascript
复制
hello i'm hong!null
com.Student@b1bc7ed
hello i'm hong!null
com.Student@7cd84586

创建对象的步骤

其它可以先参考两个文章:

jvm的类加载器(classloader)及类的加载过程

类的加载时机

内存布局

可以先参考原来的文章:对象的内存是如何布局的?

对象结构由对象头、对象体、对齐字节所组成。

由于其他都在上文已有相关的描述,本文主要针对Mark Word 来深入。

Klass Word

Klass Word对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

Mark Word

Mark Word 默认存储对象是HashCode,分代年龄和锁标志位信息。Mark Word存储的数据会随着锁标志位的变化而变化。以下表是相关锁的锁状态

可以通过查看 jdk8 markOop.hpp 源码注释。

HotSpot虚拟机对象头Mark Word(32位)

锁状态

25bit

4bit

1bit

2bit

23bit

2bit

偏向模式

标志位

未锁定

对象哈希码

分代年龄

0

01

偏向锁

线程ID

Epoch

分代年龄

1

01

轻量级锁定

指向调用栈中锁记录的指针

00

重量级锁定(锁膨胀)

指向重量级锁的指针

10

GC标记

11

HotSpot虚拟机对象头Mark Word(64位)

锁状态

56bit

1bit

4bit

1bit

2bit

是否偏向锁

标志位

未锁定

unused:25bit

对象hashCode:31bit

unused

分代年龄

0

01

偏向锁

线程ID:54bit

Epoch:2bit

unused

分代年龄

1

01

轻量级锁定

指向调用栈中锁记录的指针(ptr_to_lock_record)

00

重量级锁定(锁膨胀)

指向互斥锁(重量级锁)的指针(ptr_to_heavyweight_monitor)

10

GC标记

11

相关说明:

标志位:区分锁的状态,最后两位为11时表示为GC回收状态。

是否偏向锁:由于未锁定和偏向锁的标志位都是01,所以引入一位是否偏向锁(biased_lock)来判断,当等于1时,则是偏向锁。

分代年龄:表示对象被GC的次数,当该次数到达阈值的时候,对象就会转移到老年代。

对象hashCode:运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中。

偏向锁的线程ID1:偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。在后面的操作中,就无需再进行尝试获取锁的动作。

epoch:偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁。

ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针。

ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针。

相关校验过程

引入jar包

代码语言:javascript
复制
<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>0.8</version>
</dependency>

jol-core 常用的三个方法

ClassLayout.parseInstance(object).toPrintable():查看对象内部信息.

GraphLayout.parseInstance(object).toPrintable():查看对象外部信息,包括引用的对象.

GraphLayout.parseInstance(object).totalSize():查看对象总大小.

普通对象

代码语言:javascript
复制
package com;

import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import org.objenesis.instantiator.ObjectInstantiator;
import org.openjdk.jol.info.ClassLayout;

import java.io.*;

/**
 * @author: csh
 * @Date: 2021/2/8 17:47
 * @Description:验证equals错误
 */
public class Student implements Serializable {
    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;
    }

    public void say() {
        System.out.println("hello i'm hong!"+this.age);
    }


    public static void main(String[] args) throws Exception {

        Student student = new Student();
        System.out.println(ClassLayout.parseInstance(student).toPrintable());


    }
}

默认开启压缩指针

代码语言:javascript
复制
com.Student object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4                     (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                     (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4    java.lang.String Student.name null
     16     4   java.lang.Integer Student.age null
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

可以看到有 OFFSET、SIZE、TYPE DESCRIPTION、VALUE 这几个名词头,它们的含义分别是

OFFSET:偏移地址,单位字节;

SIZE:占用的内存大小,单位为字节;

TYPE DESCRIPTION:类型描述,其中object header为对象头;

VALUE:对应内存中当前存储的值,二进制32位;

可以看到对象实例占了24 byte(192 bit(位)) ,对象头占12 byte(96 bit),其中对象头mark word 占据了8 byte(64 bit), kclass point占据4 byte(32 bit),填充4 byte(32 bit),这里直接证明:

padding为8个字节成整数倍;

jdk8默认开始压缩指针;

关闭压缩指针

代码语言:javascript
复制
-XX:-UseCompressedOops
代码语言:javascript
复制
com.Student object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4                     (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                     (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header) 00 34 b6 1c (00000000 00110100 10110110 00011100) (481702912)
     12     4                     (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     8    java.lang.String Student.name null
     24     8   java.lang.Integer Student.age null
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

可以发现关闭了压缩指针这个体积变成32 bytes(256 bit),32-24=8 足足大了64 bit,所以,可以得知,没有开启压缩指针是没有填充的。

数组对象

代码语言:javascript
复制
int[] arr = {1,2,3};
System.out.println(ClassLayout.parseInstance(arr).toPrintable());

结果

开启压缩

代码语言:javascript
复制
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
     16    12    int [I.<elements> N/A
     28 4 (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

可以看到对象实例占了32 byte(256 bit(位)) ,对象头占12 byte(96 bit),其中对象头mark word 占据了8 byte(64 bit), kclass point占据4 byte(32 bit),实例对象占据 12 byte(96 bit),填充4 byte(32 bit):

关闭压缩

代码语言:javascript
复制
-XX:-UseCompressedOops
代码语言:javascript
复制
OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 68 0b d0 1b (01101000 00001011 11010000 00011011) (466619240)
     12     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     4        (object header) 03 00 00 00 (00000011 00000000 00000000 00000000) (3)
     20     4        (alignment/padding gap) 
     24    12    int [I.<elements> N/A
     36 4 (loss due to the next object alignment)
Instance size: 40 bytes
Space losses: 4 bytes internal + 4 bytes external = 8 bytes total

可以发现关闭了压缩指针这个体积变成40 bytes(320 bit),40-32=8 足足大了64 bit,所以,可以得知,没有开启压缩指针是没有填充的。

JVM锁的升级过程

在jdk1.6的时候还只有重量级锁,到了jdk1.7引入了偏向锁、轻量级锁,至此以后,锁分为:无锁、偏向锁、轻量级锁、重量级锁。

不同的锁,意味意性能和开销都不一样,以及锁是如何慢慢升级为重量级锁的呢?

创建一个新对象默认为无锁,尝试加偏向锁,当偏向锁竞争稍微激烈则升级为轻量级锁,如果竟争再加剧则升级为重量级锁。

验证锁升级

HotSpot虚拟机对象头Mark Word(64位)

锁状态

56bit

1bit

4bit

1bit

2bit

是否偏向锁

标志位

未锁定

unused:25bit

对象hashCode:31bit

unused

分代年龄

0

01

偏向锁

线程ID:54bit

Epoch:2bit

unused

分代年龄

1

01

轻量级锁定

指向调用栈中锁记录的指针(ptr_to_lock_record)

00

重量级锁定(锁膨胀)

指向互斥锁(重量级锁)的指针(ptr_to_heavyweight_monitor)

10

GC标记

11

无锁状态

未锁定

unused:25bit

对象hashCode:31bit

unused

分代年龄

0

01

代码语言:javascript
复制
package com.lock;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author: csh
 * @Date: 2021/5/11 11:03
 * @Description:无锁
 */
public class UnLock {
    public static void main(String[] args) {
        UnLock unLock = new UnLock();
        System.out.println("前:"+ClassLayout.parseInstance(unLock).toPrintable());
        System.out.println(unLock.hashCode());
        System.out.println("后:"+ClassLayout.parseInstance(unLock).toPrintable());
    }
}

结果

代码语言:javascript
复制
前:com.lock.UnLock object internals:
 OFFSET  SIZE   TYPE DESCRIPTION VALUE
      0     4        (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

999661724
后:com.lock.UnLock object internals:
 OFFSET  SIZE   TYPE DESCRIPTION VALUE
      0     4        (object header) 01 9c a0 95 (00000001 10011100 10100000 10010101) (-1784636415)
      4     4        (object header) 3b 00 00 00 (00111011 00000000 00000000 00000000) (59)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

可以看出,没有计算hashcode的时候这个值还是00,而计算后变成01,这个就与上面的无锁一致!

偏向锁状态&匿名偏向锁状态

偏向锁

线程ID:54bit

Epoch:2bit

unused

分代年龄

1

01

当一个对象已经计算过 identity hash code(例如存在有执行 hashcode 的操作),它就无法进入偏向锁状态

当一个对象正在处于偏向锁状态,如果需要计算其 identify hash code 的话,则它的偏向锁会被撤销,并且锁会膨胀为重量锁

重量锁的实现中,ObjectMonitor 类里有字段可以记录非加锁状态下的 mark word,其中可以存储 identity hash code 的值

由于是小端排序,所以要反着来。

代码语言:javascript
复制
package com.lock;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author: csh
 * @Date: 2021/5/11 11:16
 * @Description:偏向锁
 */
public class PartialLock {

    static PartialLock partialLock = new PartialLock();

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("未锁"+ClassLayout.parseInstance(partialLock).toPrintable());
        synchronized (partialLock) {
            System.out.println("加锁"+ClassLayout.parseInstance(partialLock).toPrintable());
        }
    }
}

结果:

代码语言:javascript
复制
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -Dvisualvm.id=1104954274125300 -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\lib\idea_rt.jar=58652:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\ideaWorkSpace\jdk8\target\classes;D:\mvn\junit\junit\4.13.2\junit-4.13.2.jar;D:\mvn\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\mvn\org\objenesis\objenesis\3.0.1\objenesis-3.0.1.jar;D:\mvn\org\openjdk\jol\jol-core\0.8\jol-core-0.8.jar" com.lock.PartialLock
未锁com.lock.PartialLock object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4        (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

加锁com.lock.PartialLock object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4        (object header) 05 38 20 03 (00000101 00111000 00100000 00000011) (52443141)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Process finished with exit code 0

颜色代表:是否偏向、锁标记

小端排序:

00000101 00111000 00100000 00000011 00000000 00000000 00000000 00000000

大端排序:

00000000 00000000 00000000 00000000 00000011 00100000 00111000 00000101

轻量级锁

轻量级锁定

指向调用栈中锁记录的指针(ptr_to_lock_record)

00

代码语言:javascript
复制
package com.lock;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author: csh
 * @Date: 2021/5/11 14:38
 * @Description:轻量级锁状态
 */
public class GentlyLock {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        final GentlyLock gentlyLock = new GentlyLock();

        synchronized (gentlyLock){
            System.out.println("偏向锁"+ClassLayout.parseInstance(gentlyLock).toPrintable());
        }
        new Thread(() -> {
            synchronized (gentlyLock){
                System.out.println("轻量级锁"+ClassLayout.parseInstance(gentlyLock).toPrintable());
            }
        }).start();
    }
}

结果

代码语言:javascript
复制
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -Dvisualvm.id=1106556182703499 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\lib\idea_rt.jar=60327:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\ideaWorkSpace\jdk8\target\classes;D:\mvn\junit\junit\4.13.2\junit-4.13.2.jar;D:\mvn\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\mvn\org\objenesis\objenesis\3.0.1\objenesis-3.0.1.jar;D:\mvn\org\openjdk\jol\jol-core\0.8\jol-core-0.8.jar" com.lock.GentlyLock
偏向锁com.lock.GentlyLock object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4        (object header) 05 38 e3 02 (00000101 00111000 11100011 00000010) (48445445)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

轻量级锁com.lock.GentlyLock object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4        (object header) 80 f7 01 20 (10000000 11110111 00000001 00100000) (536999808)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Process finished with exit code 0

颜色代表:是否偏向、锁标记

偏向锁

小端排序:00000101 00111000 11100011 00000010 00000000 00000000 00000000 00000000

大端排序:00000000 00000000 00000000 00000000 00000010 11100011 00111000 00000101

轻量级锁

小端排序:10000000 11110111 00000001 00100000 00000000 00000000 00000000 00000000

大端排序:00000000 00000000 00000000 00000000 00100000 00000001 11110111 10000000

重量级锁

重量级锁定(锁膨胀)

指向互斥锁(重量级锁)的指针(ptr_to_heavyweight_monitor)

10

代码语言:javascript
复制
package com.lock;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author: csh
 * @Date: 2021/5/11 17:34
 * @Description:重量级锁
 */
public class WeightLock {
    static WeightLock weightLock = new WeightLock();
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(1000);
        new Thread(()->{
            synchronized (weightLock) {
                try {
                    weightLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ClassLayout.parseInstance(weightLock).toPrintable());
            }
        }).start();
        Thread.sleep(5000);
        synchronized (weightLock){
            weightLock.notifyAll();
        }
    }
}

结果

代码语言:javascript
复制
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" -Dvisualvm.id=1117453392924400 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\lib\idea_rt.jar=55196:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;D:\ideaWorkSpace\jdk8\target\classes;D:\mvn\junit\junit\4.13.2\junit-4.13.2.jar;D:\mvn\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\mvn\org\objenesis\objenesis\3.0.1\objenesis-3.0.1.jar;D:\mvn\org\openjdk\jol\jol-core\0.8\jol-core-0.8.jar" com.lock.WeightLock
com.lock.WeightLock object internals:
 OFFSET SIZE TYPE DESCRIPTION VALUE
      0     4        (object header) ea 36 5e 1c (11101010 00110110 01011110 00011100) (475936490)
      4     4        (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Process finished with exit code 0

颜色代表:是否偏向、锁标记

小端排序:

11101010 00110110 01011110 00011100 00000000 00000000 00000000 00000000

大端排序:

00000000 00000000 00000000 00000000 00011100 01011110 00110110 11101010

总结:从上面可以验证到不同的锁升级后的验证方式以及展现。

最后

重新了解关于对象与内存布局,特别这块锁升级,是多线程的基础以及从jvm层面来学习锁这块,会更容易理解关于synchronized 以及后续多线程深入的知识及性能考量。本文参考了大量的相关可靠性文档以及相关的学习资料,耗费比较大的精力,因为这块的确比较深入了,也非常值得学习。

参考文献

https://www.cnblogs.com/yxym2016/p/12920315.html

https://blog.csdn.net/qq_26542493/article/details/90938070

https://gorden5566.com/post/1019.html

https://blog.csdn.net/qq_36434742/article/details/106854061

https://zhuanlan.zhihu.com/p/50984945

https://www.cnblogs.com/jajian/p/13681781.html

https://www.cnblogs.com/cxuanBlog/p/11684390.html

https://www.cnblogs.com/dongl961230/p/13275011.html https://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html

https://www.yuque.com/u500486/java/ga07ka

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术趋势 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档