## Java对象头(64位虚拟机)
- 整个对象头一共有128位,Mark Word有64位,Klass Word有64位,但是Klass Word因为指针压缩的原因被压缩为32位,使用对象头一共有96位,而且现在我们更需要关注的是前64位。至于Klass Word指向了方法区的模板类,字节码。
- 对象头还不是一成不变的,就表格可以看出,对象的状态会改变对象头的数值,这里我们分为5个状态,分别是无锁(001)、偏向锁(101)、轻量锁(00)、重量锁(10)和被gc标记(11)的对象。
- ![64位虚拟机对象头.png](https://upload-images.jianshu.io/upload_images/19480260-d33ae97a21f2158e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 对象头占据内存
- 例如:int是4个字节,Integer对象的Mark Word8字节,Klass Word8个字节(指针压缩后4字节),存储占4字节,存储一个数需要占8(Mark Word)+4(Klass Word)+4(存储占4字节)=16字节
**验证new Integer(1)占据内存大小**
```
@Slf4j(topic = "ants.TestIntegerSize")
public class TestIntegerSize {
public static void main(String[] args) {
Integer i = new Integer(1);
log.debug(ClassLayout.parseInstance(i).toPrintable());
}
}
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
2020-04-12 17:45:45.202 [main] DEBUG ants.TestIntegerSize 16 - java.lang.Integer 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) ae 22 00 f8 (10101110 00100010 00000000 11111000) (-134208850)
12 4 int Integer.value 1
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
```
- 由上面控制台结果分析,new Integer(1)会占据16字节,而我们知道int i=1只会占四个字节。
## Monitor(锁) 监视器|管程
- 每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级锁)之后,该对象头的Mark Word就被设置指向Monitor对象指针。
![线程-对象-Monitor关系.png](https://upload-images.jianshu.io/upload_images/19480260-af51190166c05ef3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
0. synchronized(obj)使用对象锁(obj)和Monitor对象一一对应
1. 刚开始Monitor中Owner为null
2. 当Thread-2执行synchronized就会将Monitor的所有者Owner置为Thread-2,Monitor中只能有一个Owner
3. Thread在上锁过程中,如果Thread-3,thread-4,thread-5也来执行synchronized(obj),就会进入EntryList中进阻塞(BLOCKED)状态
4. Thread-2执行完同步代码块内容,然后唤醒EntryList中等待的线程来竞争锁,竞争时是非公平的(synchronized没有提供公平锁,非公平是指如果Thread-2释放锁的时候来了Thread-6,那么Thread-3和Thread-6将同时有机会获得obj对象)
5. 图中Thread-0,Therad-1是之前获得过锁,但是条件不满足进入WAITING状态的线程(跟wait-notify相关)
### 注意
- synchronized必须是关联统一个对象时,才会出现上面结果
- 不加synchronized不会关联monitor监视器,不会出现上面结果
### synchronized原理
![代码.png](https://upload-images.jianshu.io/upload_images/19480260-45e4a4540dde0f81.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![代码中对应的字节码.png](https://upload-images.jianshu.io/upload_images/19480260-6c3091eb7efffab0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- dup:将lock复制一份
- Exception table:检测一定范围,如果范围内出现异常做处理
- 6-16-19-any,表示如果在6-6行出现异常,java会在19行开始执行,同样保存lock引用,将对象头重置,唤醒EntryList,让其他线程竞争锁,最后将处理不了的异常抛出去
- 在字节码中可以看出,即使synchronized中出现异常,synchronized也会释放锁
### 不同锁
- Monitor
- 轻量级锁
- 偏向锁
- 批量重偏向(一个类的偏向锁撤销达到20阈值)
- 一个类的偏向锁撤销达到40阈值,对象升级为轻量级锁
#### 轻量级锁
- 使用场景:如果一个对象虽然有多线程访问,单多线程访问的时间是错开的(没有线程竞争),name可以用轻量级锁来优化。
- 语法:轻量级锁对使用这透明,仍然使用synchronized
**轻量级锁加锁、解锁流程**
```
public class LightLock {
static final Object object = new Object();
public static void method1(){
synchronized (object){
// 同步代码块1
method2();
}
}
public static void method2(){
synchronized (object){
// 同步代码块2
}
}
}
```
1. 创建锁记录(Lock Record)对象,每个线程的栈帧都会包含一个锁记录,内部可以存储锁定对象的Mark Word
![image.png](https://upload-images.jianshu.io/upload_images/19480260-0cdeb93ec46bf7a5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2. 让锁记录中Object Reference指向锁对象,并尝试使用cas替换Object的Mark Word的值存入锁记录
![image.png](https://upload-images.jianshu.io/upload_images/19480260-d2b8aa6481000ba1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
3. 如果替换成功,对象头中存储了锁记录地址和状态00(代表轻量级锁),表示该线程给对象加锁,--(object对象头中Mark Word发生变化,原先01无锁状态变成00轻量级锁状态,另外分代年龄等信息变为锁记录地址),--(锁记录里面将记录hash码、分代年龄等信息)(--锁记录和Mark Word信息互换)
![image.png](https://upload-images.jianshu.io/upload_images/19480260-2df7e6da608599aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4. 如果cas失败,分为两种情况
4.1 如果是其他线程已经持有了该Object的轻量级锁,这时表名有竞争,进入锁膨胀过程
4.2 如果是自己执行了synchronized表示锁重入,那么再添加一条Lock Record作为重入的计数
![image.png](https://upload-images.jianshu.io/upload_images/19480260-9ecec6a6953defce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
5. 当退出synchronized代码块(解锁时),如果取值为null的锁记录,这时重置锁记录,表示重入计数减一
![image.png](https://upload-images.jianshu.io/upload_images/19480260-8457230b5554ad13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
6. 当退出synchronized代码块(解锁时),如果取值不为null的锁记录,这时cas将Mark Word的值恢复给对象头
- 成功,则解锁成功
- 失败,说明轻量