对象元信息
在JVM当中,一个Java对象的内存主要有三块:
对象头,对象头包含两部分数据,分别是Mark word和类型指针(Kclass pointer)。
实例数据,就是我们在类当中定义的各种数据。
对齐填充,JVM在实现的时候要求每一个对象所占有的内存大小都需要是8字节的整数倍,如果一个对象的数据所占有的内存大小不够8字节的整数倍,那就需要进行填充,补齐到8字节,比如说如果一个对象站60字节,那么最终会填充到64字节。
而与我们要谈到的synchronized锁升级原理密切相关的是Mark word,这个字段主要是存储对象运行时的数据,比如说对象的Hashcode、GC的分代年龄、持有锁的线程等等。而Kclass pointer主要是用于指向对象的类,主要是表示这个对象是属于哪一个类,主要是寻找类的元数据。
在32位Java虚拟机当中Mark word有4个字节一共32个比特位,其内容如下:
我们在使用synchronized时,如果我们是将synchronized用在同步代码块,我们需要一个锁对象。对于这个锁对象来说一开始还没有线程执行到同步代码块时,这个4个字节的内容如上图所示,其中有25个比特用来存储哈希值,4个比特用来存储垃圾回收的分代年龄,剩下三个比特其中第一个用来表示当前的锁状态是否为偏向锁,最后的两个比特表示当前的锁是哪一种状态:
如果最后三个比特是:001,则说明锁状态是没有锁。
如果最后三个比特是:101,则说明锁状态是偏向锁。
如果最后两个比特是:00, 则说明锁状态是轻量级锁。
如果最后两个比特是:10, 则说明锁状态是重量级锁。
无锁->偏向锁->轻量级锁->重量级锁
不加锁->一个线程持有锁->多个线程持有锁(cas)->cas一定次数无法取到锁