由java对象头到锁升降级

前言

昨天听取了某大咖直播视频,受益匪浅,之前也陆续了解过一些,但一直迷惑,昨天解惑,于是记录下来。接下来所有内容均针对64位操作系统进行分析。

1.JAVA对象头

在《深入了解JAVA虚拟机》中有一段内容,描述的正是JAVA对象构成,其中对象头由MarkWord、Classpointer、实例数据(对象属性)、对象对其补充、以及数组长度(数组对象才有)构成。每个JAVA对象都有一个通用的对象头结构,里面包含了对象布局、GC状态、加锁状态、hashcode,本文也将通过synchronized关键字分析对象的偏向锁、轻量级锁、重量级锁,以及锁降级,锁升级。classpointer包含了对象指针,指向该实例是属于哪一个类的实例。

在64位操作系统中,对象头由64位12个字节组成,其中markword占8字节,classpointer占4字节。

1.2怎样查看一个对象的对象头

OpenJDK提供了一个工具JOL,让我们能够清晰的了解到对象的对象头构成。通过maven直接引入相关依赖即可

        <!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.10</version>
        </dependency>

具体对象头信息如下示例代码:

public class T {

    public static void main(String[] args) {
        MarkWord markWord = MarkWord.builder().age(11).sex(true).price(22.2).build();
        System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
    }

    

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    static class MarkWord{
        private Integer age;

        private boolean sex;

        private double price;


    }
}

运行结果如下:

 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord.sex                              true
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord.price                            22.2
     24     4   java.lang.Integer MarkWord.age                              11
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

由运行结果可以看出,该对象实例一共占据了32字节

 

在java中boolean占据1个字节,double 8个字节,Integer4个字节,该对象实例中markword8个字节,classpointer4字节,因此需要填充7个字节,才能满足最终对象实例大小是8的倍数,能被8整除。(HotSpot虚拟机要求对象起始地址必须是8bit的整数倍)。

若是删除对象MarkWord的属性,重新运行,可以得到如下的结果,此时可以发现,对象只有16字节,为8的整数倍。而填充对齐只有4字节,因为markword8字节,classpointer4字节,需要凑齐8字节的整数倍,所以需要填充4字节。(填充对齐因此也可以理解为占位符)。

 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes

接下来,解惑,到底哪一段是对象中的hashcode呢?改良过后的代码如下:

public class T2 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        System.out.println("before hash");
        MarkWord2 markWord = MarkWord2.builder().age(11).price(22.22).sex(true).build();
        //未计算hash之前的对象头
        System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
        //JVM计算的hashcode
        System.out.println("JVM calculated hashcode---------0x"+Integer.toHexString(markWord.hashCode()));
        HashUtil.countHash(markWord);
        System.out.println("after hash");
        //计算hashcode之后的对象头
        System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
    }
}

然后我们创建一个HashUtil类,用于将对象头的56个bit的值转为16进制的hashcode

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class HashUtil {

    public static void countHash(Object object) throws NoSuchFieldException, IllegalAccessException {
        // 手动计算HashCode
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);
        long hashCode = 0;
        //!!!注意,由于是小端存储,我们将从后往前计算
        for (long index = 7; index > 0; index--){
            //取Mark Word中的每一个Byte进行计算
            hashCode |= (unsafe.getByte(object, index) & 0xFF) << ((index -1) *8);
        }
        String code = Long.toHexString(hashCode);
        System.out.println("HashUtil calculated hashcode ---0x" + code);
    }
}

运行结果如下:

before hash
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)                           43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total


JVM calculated hashcode---------0x3389e
HashUtil calculated hashcode ---0x0
after hash
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           01 89 99 ee (00000001 10001001 10011001 11101110) (-291927807)
      4     4                     (object header)                           3f 00 00 00 (00111111 00000000 00000000 00000000) (63)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             true
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           22.22
     24     4   java.lang.Integer MarkWord2.age                             11
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

有前面的实例可以得出,对象头一共占据了12个字节,其中markword占据了8个字节,classpointer占据了4个字节。markword包含了锁信息,年龄带,hashcode、gc信息、对象布局等。那么,经过上面的例子通过调用hashcode前后(通过两种方法分别进行hashcode计算,得出来的hashcode一致),比较markword的数据,发现后面7字节,56位保存的才是hashcode数据(有变化的是后面的部分,56字节,7位, 7x8=56)

得出结论,第一个字节当中的8bit分别存储的就是年龄分带、偏向锁状态、对象状态、这些数据

2.偏向锁

关于对象的状态,一共有五种,无锁、偏向锁、轻量级锁(自旋锁)、重量锁、GC标记,然而对象状态只有两个位,因此最多只有00、01、10、11。那么我们要如何用2个bit表示的4个状态呢?很简单JVM通过把偏向锁和无锁表示为同一状态即都是01,然后通过前一个bit的偏向锁标示来分辨到底是无锁还是偏向锁。

运行结果如下:

-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

加锁了.
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           80 eb 76 02 (10000000 11101011 01110110 00000010) (41347968)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

-----加锁之后-----
demo.markword.MarkWord2 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

看锁状态,只需要看第一个字节,最后三位,倒数第三位为0代表非偏向锁 1代表偏向锁,倒数两位00代表轻量级锁,10重量级锁,01偏向锁。

很显然,在加锁的过程中,第一位的8个bit 00000000 后三位是000,表示是轻量级锁。在有且只有一个线程去获取synchronized中的对象的时候,该对象的锁为偏向锁,但是上面你的结果却不符合我们的预期,因为,JVM的偏向锁有延迟启动机制,每个对象并不会在一开始就获得偏向锁,需要程序启动后4-5秒才能够获得偏向锁。我们在上面的例子中加入线程休眠5秒(可以配置JVM 启动参数取消偏向锁延迟-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0)


import demo.markword.MarkWord2;
import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.TimeUnit;


public class LockDemo {

    static MarkWord2 markWord2;

    public static void main(String[] args) throws InterruptedException {
        TimeUnit.SECONDS.sleep(5);
        markWord2 = new MarkWord2();
        System.out.println("-----加锁之前-----");
        System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
        sync();
        System.out.println("-----加锁之后-----");
        System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
    }

    private static void sync(){
        synchronized(markWord2){
            System.out.println("加锁了.");
            System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
        }
    }

}

加入线程休眠代码后,运行结果如下:

-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

加锁了.
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           05 80 31 02 (00000101 10000000 00110001 00000010) (36798469)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

-----加锁之后-----
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           05 80 31 02 (00000101 10000000 00110001 00000010) (36798469)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

可以清晰地看到,在休眠五秒后,过了偏向锁的延迟时间,第一字节的8bit为00000101,后三位里,倒数第三位是1表示偏向锁

倒数两位为01表示偏向锁。此时锁状态成了偏向锁。和轻量级锁重量级锁不同的是,偏向锁每次退出后,状态仍然是偏向锁。

在单线程的情况下,使用synchronized并不会给被synchronized修饰的对象加锁,而只是在被修饰的对象对象头markdown中,将当前线程的线程ID赋予进去。

3.轻量级锁

轻量级锁同前面的实例

public class LockDemo {

    static MarkWord2 markWord2;

    public static void main(String[] args) throws InterruptedException {
//        TimeUnit.SECONDS.sleep(5);
        markWord2 = new MarkWord2();
        System.out.println("-----加锁之前-----");
        System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());

        sync();
        System.out.println("-----加锁之后-----");
        System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
    }




    private static void sync(){
        synchronized(markWord2){
            System.out.println("-----加锁了-----");
            System.out.println(ClassLayout.parseInstance(markWord2).toPrintable());
        }
    }

}

此时,去掉线程休眠的代码,执行结果如下

-----加锁之前-----
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 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)                           43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

-----加锁了-----
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           10 ee 69 02 (00010000 11101110 01101001 00000010) (40496656)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

-----加锁之后-----
demo.markword.MarkWord2 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)                           43 d8 00 f8 (01000011 11011000 00000000 11111000) (-134162365)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

Disconnected from the target VM, address: '127.0.0.1:56857', transport: 'socket'

一开始,由于偏向锁加载延迟,第一字节的8位为00000001 倒数第三位为0,代表非偏向锁,但是后两位是01 代表一开始是无锁。后面加锁的时候成了00010000 变成了轻量级锁,执行完成后 00000001 又成了无锁。偏向锁如果有线程争用,就会升级为自旋锁(轻量级锁)。当自旋锁自旋十次(或CPU数量一半)就会升级为重量级锁。

4.重量级锁

在多线程并发争抢锁的情况下,轻量级锁升级成了重量级锁

import demo.markword.MarkWord2;
import org.openjdk.jol.info.ClassLayout;

public class ZhongLockDemo {

    static MarkWord2 markWord = null;

    public static void main(String[] args) {
        markWord = new MarkWord2();
        System.out.println("before lock");
        System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
        new Thread(()->{
            sync();
        }).start();
        new Thread(()->{
            sync();
        }).start();

        System.out.println("after lock");
        System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
    }

    public static void sync(){
        synchronized (markWord){
            System.out.println(Thread.currentThread().getName()+" is locking");
            System.out.println(ClassLayout.parseInstance(markWord).toPrintable());
        }
    }


}

 

before lock
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. Unable to attach even with module exceptions: [org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。, org.openjdk.jol.vm.sa.SASupportException: Cannot run program "C:/Program Files/Java/jdk1.8.0_201/jre/bin/java": CreateProcess error=206, 文件名或扩展名太长。]
demo.markword.MarkWord2 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)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

Thread-1 is locking
after lock
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

Thread-2 is locking
demo.markword.MarkWord2 object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
      0     4                     (object header)                           1a df fd 04 (00011010 11011111 11111101 00000100) (83746586)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                     (object header)                           43 c2 00 f8 (01000011 11000010 00000000 11111000) (-134167997)
     12     1             boolean MarkWord2.sex                             false
     13     3                     (alignment/padding gap)                  
     16     8              double MarkWord2.price                           0.0
     24     4   java.lang.Integer MarkWord2.age                             null
     28     4                     (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

总结:在一开始,只有单线程的情况下,一个线程过来,会先把自己的线程ID赋予到对象头里的markword里,表示我拿到了这个对象了,这个时候对应的锁是偏向锁,但是,JVM对于单线程的情况下,偏向锁有延迟。

当有线程竞争,这个时候,偏向锁会变成轻量级锁,其实,轻量级锁就是运用旋转,CAS,同时把自己的LR(LocalRecord放到线程栈),这个时候变成了轻量级锁,当然,这个情况下,不可能是无限自旋的,因为几个线程,对于CPU来说,压力其实并不大,但是假设是几千几万个线程,一直阻塞,不断自旋,其实CPU的压力是非常大的,当然不会让他这么干, 二十当自选次数超过十次,或超过CPU个数一半的情况下,这个时候,会升级锁成重量级锁,同时,会把该线程挂到该一个阻塞队列里面,等到真正持有该锁的线程执行完毕,释放锁后,出队,重新争抢资源。

synchronized对于锁的运用,体现在以下几个方面

1:代码层面,synchronized对对象或代码块进行加锁操作

2.底层 其实是一个MonitorEnter和MonitorExit的操作

3.旋转 ,锁升级

对于自旋锁,示例如下,JAVA提供了Atomic类,用于提供一系列原子操作

        AtomicInteger in = new AtomicInteger();
        in.incrementAndGet();
    /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

自旋,底层其实调用的是native方法,涉及到汇编相关的问题,但是,不论怎样,最最核心,涉及到的不过也就是用户态、内核态、缓存行、缓存对齐相关的东西,旋转运用到的最核心的就是lock指令和compare指令,lock才是核心,compare是非原子的操作。

补充:

到底什么时候锁会降级呢?

锁降级没什么太大的意义,因为锁降级发生在GC的时候,GC的时候,对象都即将被回收,没用了,所以说锁降级没什么太大的意义。 由于自旋锁不占用CPU,如果加锁代码执行时间短,线程数量少用自旋锁,如果执行时间长,执行数量多的代码,用系统锁。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值