目录
(5)用Java Object Layout研究锁升级过程中Markword随锁状态发生的变化
1、CAS(compare and swap)
CAS(compare and swap):关键是要保证compare和swap两个操作执行不被打断,保证CAS为原子操作。在汇编语言层次,如果操作系统是多cpu的,对CAS操作进行了加锁操作(单cpu由于同一时间只有一个进程对某个数据或内存进行操作,所以没必要加锁)。
lock cmpxchg 指令
2、synchronized的底层实现
(1)synchronized锁的状态
JDK早期,synchronized 叫做重量级锁, 因为申请锁资源必须通过内核系统调用。即通过操作系统内核调用进行加锁和解锁,加锁和解锁占用时间过长,线程多了加锁解锁就更频繁了,影响程序性能。(此处的内核调用主要涉及用户态到内核态的切换)
后来为了优化,针对线程的多少进行分层次的不同锁处理,而且尽可能的在用户态完成加锁,减少进行内核态加锁和解锁的操作
。这就是后来的改进升级。
改进后的synchronized,针对线程的多少而切换不同的锁状态。锁的状态包括无锁态(new)、偏向锁、轻量级锁(自旋锁)、重量级锁(内核级别)。 而这几种锁状态都记录在sync (object)的object中。
(2)一个对象的内存布局(hotspot实现)
一个对象的内存布局(hotspot实现)分为markword(固定8字节)、类型指针(klasspoint固定4字节)、成员变量、有时为了保证字节对齐有空区域进行补齐占位。
字节对齐(字节总数保证是8的整数倍)
- markword: 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。一般占用8字节。
- klasspoint: 固定长度4byte, 指定该对象的class类对象(默认使用-XX:+UseCompressedClassPointers 参数进行压缩,可使用-XX:-UseCompressedClassPointers关闭,则该字段在64位jvm下占用8个字节;可使用java -XX:+PrintCommandLineFlags -version 命令查看默认的或已设置的jvm参数);
- 基本变量:用于存放java八种基本类型成员变量,以4byte步长进行补齐,使用内存重排序优化空间;
- 引用变量:存放对象地址,如String,Object;占用4个字节,64位jvm上默认使用-XX:+UseCompressedOops进行压缩,可使用-XX:-UseCompressedOops进行关闭,则在64位jvm上会占用8个字节;
- 补齐:对象大小必须是8byte的整数倍,用来补齐字节数。Object o = new Object() 在内存中占用16个字节,其中最后4个是补齐;
- 数组长度:如果是数组,额外占用固定4byte存放数组长度;
(3)markword
markword用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。一般占用8字节。1字节(Byte)=8比特(bit)
下图是hotspot中markword的具体分布,不同的锁状态对应不同的值。