【转】谈谈 JVM 内部锁升级过程

一、加锁发生了什么

//System.out.println都加了锁
public void println(String x) {
  synchronized (this) {
    print(x);
    newLine();
  }
}

简单加锁发生了什么?

要弄清楚加锁之后到底发生了什么需要看一下对象创建之后再内存中的布局是个什么样的?

一个对象在 new 出来之后在内存中主要分为 4 个部分:

  • Markword 这部分其实就是加锁的核心,同时还包含的对象的一些生命信息,例如是否 GC、进过了几次 Young GC 还存活等。
  • klass pointer 记录了指向对象的 class 文件指针。
  • instance data 记录了对象里面的变量数据。
  • padding 作为对齐使用,对象在 64 位服务器版本中,规定对象内存必须要能被 8 字节整除,如果不能整除,那么就靠对齐来补。举个例子:new 出了一个对象,内存只占用 18 字节,但是规定要能被 8 整除,所以 padding=6。
    【转】谈谈 JVM 内部锁升级过程

知道了这 4 个部分之后,我们来验证一下底层。借助于第三方包 JOL = Java Object Layout java 内存布局去看看。很简单的几行代码就可以看到内存布局的样式:

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupid>org.openjdk.jol</groupid>
    <artifactid>jol-core</artifactid>
    <version>0.9</version>
</dependency>
public class JOLDemo {
    private static Object  o;
    public static void main(String[] args) {
        o = new Object();
        synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }
}

将结果打印出来:

【转】谈谈 JVM 内部锁升级过程

从输出结果看:

  1. 对象头包含了 12 个字节分为 3 行,其中前 2 行其实就是 Markword,第三行就是 klass 指针。值得注意的是在加锁前后输出从 001 变成了 000。Markword 用处:8 字节(64bit)的头记录一些信息,锁就是修改了 Markword 的内容 8 字节(64bit)的头记录一些信息,锁就是修改了markword的内容字节(64bit)的头记录一些信息。从 001 无锁状态,变成了 00 轻量级锁状态。
    【转】谈谈 JVM 内部锁升级过程
  2. new 出一个 object 对象,占用 16 个字节。对象头占用 12 字节,由于 Object 中没有额外的变量,所以 instance = 0,考虑要对象内存大小要被 8 字节整除,那么 padding=4,最 后 new Object() 内存大小为 16 字节。

二、锁的升级过程

2.1 锁的升级验证

探讨锁的升级之前,先做个实验。两份代码,不同之处在于一个中途让它睡了5秒,一个没睡。看看是否有区别。

`
public class JOLDemo {
private static Object o;
public static void main(String[] args) {
o = new Object();
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}

Original: https://www.cnblogs.com/jmcui/p/14959773.html
Author: JMCui
Title: 【转】谈谈 JVM 内部锁升级过程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/619763/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球