一 为什么讲这个?
总结AQS之后,对这方面顺带的复习一下。本文从以下几个高频问题出发:
- 对象在内存中的内存布局是什么样的?
- 描述synchronized和ReentrantLock的底层实现和重入的底层原理。
- 谈谈AQS,为什么AQS底层是CAS volatile?
- 描述下锁的四种状态和锁升级过程?
- Object o = new Object() 在内存中占用多少字节?
- 自旋锁是不是一定比重量级锁效率高?
- 打开偏向锁是否效率一定会提升?
- 重量级锁到底重在哪里?
- 重量级锁什么时候比轻量级锁效率高,同样反之呢?
二 加锁发生了什么?
无意识中用到锁的情况:
代码语言:javascript复制//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。
代码语言:javascript复制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());
}
}
}