先说一下synchronized的历史吧,在jdk1.6之前synchronized就是单纯一个重量级锁,在jdk1.6的时候,有一个叫Doug Lea的大哥看不过去了,明明很多情况都是不需要重量级锁的,只需要标记一下,所以这个大哥就开发一个ReentrantLock,无竞争的情况在java层面就完成了加锁解锁,这时候吧,jdk可能觉得面子上有点那啥,便开始优化synchronized,然后就产生了我们熟知的偏向锁,轻量级锁,重量级锁。
因为网上大部分的文章写mark word的解释基本都是基于32位jdk,这里我简单介绍一下64位jdk中,mark down的组成,和锁升级过程标志位的变化。
1、java对象布局
java的对象布局是由对象头、实例数据、数据对齐组成
但实际情况并不一定,后面我们引用下面的maven依赖来进行证明
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
2、对象布局的证明
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
class A {
boolean a = true;
}
(1)在这里我们发现对象头的确是又上面三个部分组成.
(2)我们将boolean改为int后
发现没有数据填充的部分,这时候我们并不需要数据填充
(3)同理,我们将变量a删除后,没有了示例数据这一部分
3、我们可以得出结论
对齐填充并不是必然存在的,也没有什么特别的意义,他仅仅起着占位符的作用,由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头正好是8字节的倍数,因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
4、mark word
对象头由两部分组成,一部分用于存储自身的运行时数据,称之为mark word,另外一部分是类型指针,及对象指向它的类元数据的指针。这里我们只研究一下mark word
OpenJdk官网对于mard word的定义
每个对象头的第一个字。通常是一组位字段,包括同步状态和标识哈希码。也可以是指向同步相关信息的指针(具有特征性的低位编码)。在GC期间,可以包含GC状态位。
我们再看hotspot的源码,里面有一段注释
首先无锁、偏向锁、轻量锁大家应该都很清楚,但是图中的无锁可偏向和无锁不可偏向我在这里解释下,
我们知道偏向锁会在mark word中记录偏向线程的id,但是根据上图,我们可以发现,hashcode和偏向线程的id的位置是有冲突的,所以在不可偏向时就是我们计算了这个类的hashcode,这时候锁会直接升级成轻量级锁,后面我们会通过例子来证明。
5、hashcode
首先我们计算一下hashcode 再和mark word进行比较
这里我要先说明下,后面我们输出的mark word和我们期望的 在位置上是相反的,这是由于cpu的缘故,这里不做解释
jdk8中,有一个偏向锁的延迟开启,我们需要把延迟时间设置为0
-XX:BiasedLockingStartupDelay=0
首先我们可以看到 实例的hashcode和mark word里面标记的一样,并且现在是无锁不可偏向状态
下面不计算hashcode再看一下锁的标志位
6、模拟无锁升级偏向锁过程
public class B {
static A a = new A();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("t1:");
}
}
};
t1.start();
t1.join();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
class A {
}
7、模拟无锁升级轻量级锁过程
public class B {
static final A a = new A();
static volatile int flag = 1;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(){
@Override
public void run() {
synchronized (a){
System.out.println("t1");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
flag = 2;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
while (flag!=2) {
}
synchronized (a){
System.out.println("t2");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
};
t1.start();
Thread.sleep(100);
t2.start();
}
}
class A {
}