synchronized、CAS、volitale底层实现
CAS
Atomic类都使用CAS,它依赖于Unsafe类提供的compareAndSwap方法,如AtomicInteger依赖的是Unsafe的compareAndSwapInt方法,其是native的,实现在jvm源码unsafe.cpp中;
unsafe.cpp中CompareAndSwapInt中是调用了Atomic::cmpxchg函数;
-
该函数在atomic_linux_x86_.inline.hpp中使用了汇编指令:
使用了LOCK_IF_MP和cmpxchg
synchronized
- 编译时候:使用字节码monitorenter和monitorexit指令对实现;
- 运行时:JVM会对monitor做加锁优化,偏向锁-轻量级锁-重量级锁;
- lock 和cmpxchg汇编指令实现;
学会使用JOL
-
添加依赖:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
-
使用ClassLayout:
Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable());
-
分析结果:
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
普通对象(x64):
- 0~8字节:markword
- 8~12字节:class pointer (启动了压缩指针)
- 以后的都是:实例数据;
- 以8字节为单位(64位处理器):填充,loss due to the next object alignment,因为对齐而丢失的字节;
数组对象在class pointer和实例数据之间还有4个字节,表示数组的长度;
对象头信息:
64位:
32位:
锁升级(具体加锁对对象头产生的影响,看上面的图):
- 对象new出来,第一次加锁,偏向锁;
- 竞争时,将升级为轻量级锁;
- 竞争比较大,多次重试无法获取到锁或者很多线程都在尝试获取同一个轻量级锁,将升级为重量级锁;
锁消除:
简单,仅仅放个标题;
锁粗话:
简单,仅仅放个标题;
hsdis插件可以查看jvm生成的汇编码:
启动时候指定参数,将打印生成的汇编码(需要暗转hsdis插件):
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly xxx;
volitile:线程可见性、禁止重排序;
cpu超线程
内存中的数据要先加载到register中才能被cpu利用;
就是一个ALU单元 对应两组register和PC,这样省去了这两个线程之间切换时上下文切换的开销;
局部性原理:
当我们使用某个数据时,很有可能会使用相邻的数据;
cache line(64个字节)
缓存失效是以缓存行为单位的;
disruptor 环形队列,单机最快的队列,使用到了缓存行对齐,即让不同的volatile 变量存放在不同的缓存行中,提升效率;
MESI Cache一致性协议(X86 cpu):
每个缓存行有四种状态:
Modified[图片上传中...(32bitObjectHeader.jpg-c9214c-1594222995296-0)]
Exclusive
Shared
Invalid
跨越多个缓存行的数据,依然必须使用总线锁;
乱序执行:
int x,y,a,b;
new Thread(){
run(){
a=1;
x=b;
}
}.start();
new Thread(){
run(){
b=1;
y=a;
}
}.start();
如果没有乱序执行,那么x和y不会出现0和0的组合;
new 对象分几个步骤(加上指令重排序,所以DCL需要volatile)
- new 分配空间(这里的new类似于malloc);
- init方法(构造方法);
- astore_1();
volatile解决指令重排序
语言级别:volatile;
字节码:ACC_VOLATILE
-
JVM:内存屏障,可以保证屏障前面的不会被重排序到后面去
LoadLoad 屏障:屏障前Load操作和屏障后Load操作不能重排序,以下类似;
StoreStore
LoadStore
-
StoreLoad
//JVM保证volatile禁止指令重排序; StoreStoreBarrier volatile写操作; StoreLoadBarrier LoadLoadBarrier volatile读操作; LoadStoreBarrier
hotspot:bytecodeinterpreter.cpp中实现:OrderAccess::fence();
linux:orderaccess_linux_x86.inline.hpp还是lock实现的,没有指定对象,直接锁了总线;