阿里P9——0x80的执行过程。
【硬件】:
Lock指令在执行后面指令的时候锁定一个北桥信号(不采用锁总线的方式)。
某些状态下上锁的话 , 是无需向操作系统申请的,只需要在用户态就可以解决问题。
一个对象在内存中如何分布这件事情,是和特定的JVM虚拟机实现有关系的,我们今天讲的主要是Hotspot的实现 ,其它的虚拟机有可能不是这种实现。
markword 8、 classpointer 4 、 instance data(看实际的变量类型)、 padding(补齐)。
其中,最重要的信息就是——锁信息。
通过markword的后几位标志来实现升级的;
//当我们new出一个普通对象的时候,它有可能是两种状态,两种状态的意思是——普通对象/匿名偏向 两种类型的markword是不同的形式,new Object就是一个普通对象。
我们new Object , 一旦我给这个对象加上了synchronized关键字的时候 , 它会首先升级成偏向锁 ,竞争一旦激烈就会升级成轻量级锁,轻量级锁又叫做自旋锁,竞争再加剧会变成重量级锁——也就是一开始我们提到的,向操作系统老大申请的锁。
偏向锁没有启动 , 普通对象也是有可能直接升级成轻量级锁的。
00——轻量级锁;
10——重量级锁;
11——GC标记信息;
01——必须得靠偏向锁位来区分。
001——无锁状态。
101——偏向锁状态。
偏向锁 和 轻量级锁 都是用户空间锁(用户态度锁)—— 我不需要和操作系统打交道。
里面的方法是全都加synchronized的。
markWord中放的是当前线程指针,所谓的偏向锁是没有必要设计锁竞争机制的,第一个访问这把锁的线程直接把自己的线程ID往上一贴就完了。总而言之是贴上当前线程的标志。
先撤销偏向锁之前贴上的线程标识 ,撤销掉之后进行竞争 ,竞争的方式就是——自旋的竞争 ;
每个线程在它的线程栈中生成一个LR(锁记录),将这个LR贴到锁上,锁上的指针指向哪一个线程的LR , 就表示哪个线程持有这把锁。另外的线程只能用CAS机制继续竞争。
//我这把锁必须得向操作系统去申请;MarkWord中实际记录的是一个ObjectMoint——实际就是JVM空间写的一个C++对象 , 而这个C++对象它内部去访问的时候是需要通过操作系统,经过操作系统之后拿到操作系统对应的那一把锁。
InterpreterRuntime::monitorenter方法。
当我们在Java文件中写了synchronized{…} 代码块之后,在汇编语句中就会出现:
monitorenter——原 { 位置处,锁开始。
monitorexit—–—原 } 位置出,锁结束。
最后一个monitorexit—–—产生任何异常的话。
//最后一个monitorexit是表示——产生了异常,如果发生异常自动释放锁。
//如果使用了偏向锁 ( 618行的判断 ), 也就是如果偏向锁打开了 ,fast_enter——快速地竞争锁;否则slow_enter。
//首先进入自旋;——升级为自旋锁。
//如果自旋锁不成——锁膨胀。inflate方法——膨胀为重量级锁。
synchronizer.cpp 中。
sychronized是可重入锁。
重入次数必须记录,因为要解锁几次必须得对应。
重入次数记录在哪里呢???
——不同锁的实现是不一样的,偏向锁记录在线程栈里,每增加一次,LR+1。
偏向锁、自旋锁 -> 线程栈 -> LR+1 。
重量级锁 -> ObjectMonitor 字段上。
方法m( ) 是 synchronized 修饰的 ,方法m加的锁是O , 方法m里面调用了方法 n( ) , 方法n( ) 加的锁也是O , 所以就相当于给锁O上了两次。
竞争加剧,有线程超过10次自旋,或者自旋线程数超过CPU核数的一半。
自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗。
//时间都花在线程的自旋上了。
重量级锁里面有各种各样的队列。每个队列的作用都是不同的(竞争 / 执行 / 等待)。
一个线程想申请一把重量级锁,会进入一个队列里。假如10000个线程在等待,这10000个线程会在WaitSet里 ,不需要消耗CPU时间,所以在竞争超级激烈的时候重量级锁会比自旋锁更合适。Linux内核对于进程的调度叫做CFS 。
重量级锁有等待队列,所有拿不到锁的进入等待队列,不需要消耗CPU资源。
不一定。
在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销,这时候直接关闭偏向锁,这时候直接使用自旋锁。
JVM启动过程,会有很多线程竞争(明确),所以默认情况启动时不打开偏向锁,过一段儿时间再打开。
//偏向锁启动的时候,默认有一个时延。
刚开始偏向锁还没有偏向任何一个线程,所以称之为匿名偏向。
调用了wait( ) 方法,直接进入重量级锁状态。
一个Class , 有很多的对象( 1~19 )。
//第一个线程来的时候,会把锁全都给加上。( 20个同一个类的对象 )
第二个线程来竞争这个锁的时候——它会直接升级成为《自旋锁》。从第21个对象开始,如果线程又给这个对象来上锁,上的会是偏向锁。——这个锁叫做重偏向。因为它原先偏向过第一个线程,批量重偏向。本来这个锁已经升级到了《轻量级锁》 ,JVM观测到你这个Class对应的对象竞争非常的多,这个时候给你又回到偏向锁这种效率比较高的机制上来 ,这个优化是针对于Class来进行的,它并不是针对于对象进行的,前面讲的都是针对于对象来讲的,而这个优化是针对于Class来说的,线程继续加锁 , 当加到第41个的时候,我认为加偏向锁是有问题的,这个时候就会进行批量锁撤销,会把你以前锁定的这些全部撤销,全升级为《自旋锁》。———批量锁的重偏向、批量锁的撤销。
Class生产的所有对象 ,把你对象的状态全部都换成偏向锁。
如果说你的对象特别多 ,说明竞争非常激烈 ,到第40个的时候,全部给你换成轻量级锁。
//这是一个针对Class的操作。