1.对象头
包括 mark word, klass pointer, 对齐(使大小为8的倍数),如果是数组结构还有数组长度
查看打印对象头部信息,项目中加入maven依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
<!-- <scope>provided</scope>-->
</dependency>
以下示例代码
package com.example.demo.jmm;
import org.openjdk.jol.info.ClassLayout;
public class ClassLayoutTest {
public static void main(String[] args) throws InterruptedException {
/**
* 默认开启指针压缩
* 16个字节
* markword 8
* klass Pointer 4
* 对齐填充 4
*
*
* 如果不开启指针压缩-XX:-UseCompressedOops,marword 8 klass Pointer 8
*/
System.out.println("休眠5s后 " +ClassLayout.parseInstance(new Object ()).toPrintable());
}
}
package com.example.demo.jmm;
import org.openjdk.jol.info.ClassLayout;
/**
* byte 1
* short 2
* int 4
* long 8
* float 4
* double 8
* boolean 1
* char 2
* string 4
*/
public class ClassLayoutTest {
/**
* 24个字节
* markword 8
* klass Pointer 4
* obj 1+4 5
* 对齐填充 7
*
*
* 如果不开启指针压缩-XX:-UseCompressedOops,marword 8 klass Pointer 8
*/
public static void main(String[] args) throws InterruptedException {
Object obj = new Test();
System.out.println(ClassLayout.parseInstance(obj ).toPrintable());
}
}
public class Test {
private byte p;
String x;
}
对象头部信息存放内容
偏向锁
偏向锁,大多数情况下,不存在锁竞争关系,而是由同一线程多次获得。为了消除数据在无竞争情况下锁重入(CAS操作)的开销引入偏向锁。对于没有锁竞争的场合,偏向锁有很好的优化作用
jvm6之后默认开启偏向锁,当线程休眠4S以上就会开启偏向模式。
简单代码如下
public class ClassLayoutTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("刚开始 "+ClassLayout.parseInstance(new Object()).toPrintable());
Thread.sleep(5000);
System.out.println("休眠5s后 " +ClassLayout.parseInstance(new Object()).toPrintable());
}
}
运行结果
偏向锁跟踪代码:
package com.example.demo.jmm;
import org.openjdk.jol.info.ClassLayout;
/**
* 001 无锁
* 101 偏向锁
* 000 轻量级
* 010 重量级锁
* 011 GC
*
*
*/
public class SynchronizedLockTest {
public static void main(String[] args) throws InterruptedException {
/**
* 默认开启指针压缩
* 16个字节
* markword 8
* klass Pointer 4
* 对齐填充 4
*
*
* 如果不开启指针压缩-XX:-UseCompressedOops,marword 8 klass Pointer 8
*/
System.out.println("刚开始 \n"+ ClassLayout.parseInstance(new Object()).toPrintable()); //无锁001
Thread.sleep(5000);
Object obj = new Object();
// obj.hashCode(); //调用hashcode, 会使得偏向锁失效
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
"开始执行。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
synchronized (obj){ System.out.println(Thread.currentThread().getName() +
"获取锁执行中。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
System.out.println(Thread.currentThread().getName() +
"释放锁。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
}, "thread1").start();
System.out.println( ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
}
当调用对象的hashCode方法,会使偏向锁失效。记录偏向锁的头部被hashcode码占用,导致偏向锁失效。
轻量级锁会在锁记录中记录hashcode
重量级锁会在Monitor中记录hashCode
当对象可偏向时,会升级为轻量级锁, 当对象正处于偏向锁时,会升级为重量锁,打开上面代码 调用hashcode注释,可以自行实验。
当偏向锁调用 wait()会变为重量级锁, 调用notify() 或notifyAll(),会成为轻量级锁
重量级锁
锁升级场景模拟两个线程轻微竞争场景
package com.example.demo.jmm;
import org.openjdk.jol.info.ClassLayout;
/**
* 001 无锁
* 101 偏向锁
* 000 轻量级
* 010 重量级锁
* 011 GC
*锁竞争,采用hashcode, 变为重量级锁模拟
*
*/
public class SynchronizedLockTest1 {
public static void main(String[] args) throws InterruptedException {
/**
* 默认开启指针压缩
* 16个字节
* markword 8
* klass Pointer 4
* 对齐填充 4
*
*
* 如果不开启指针压缩-XX:-UseCompressedOops,marword 8 klass Pointer 8
*/
System.out.println("刚开始 \n"+ ClassLayout.parseInstance(new Object()).toPrintable()); //无锁001
Thread.sleep(5000);
Object obj = new Object();
// obj.hashCode(); //调用hashcode, 会使得偏向锁失效
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
"开始执行。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
synchronized (obj){ System.out.println(Thread.currentThread().getName() +
"获取锁执行中。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
System.out.println(Thread.currentThread().getName() +
"释放锁。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
}, "thread1").start();
Thread.sleep(1);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +
"开始执行。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
synchronized (obj){ System.out.println(Thread.currentThread().getName() +
"获取锁执行中。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //重量级锁 000
}
System.out.println(Thread.currentThread().getName() +
"释放锁。。。\n" + ClassLayout.parseInstance(obj).toPrintable()); //无锁001
}
}, "thread2").start();
Thread.sleep(5000);
System.out.println( ClassLayout.parseInstance(obj).toPrintable()); //轻量级锁 101
}
}
运行结果
可以看到线程2获取锁时,变成了重量级锁。而重量级锁释放后又变为无锁状态