一个java对象包含:对象头,数据,对齐填充;
对象头包含:markword(如上图),类类型指针(klass word,如上图),legth(若是数组对象有这个值)
现在讲解下上图:
上图是jvm64位的对象头在各种锁状态下的信息;正常情况markword占64bit ;klass word 占64bit(一般默认开启指针压缩的:会压缩到32bit)
要分析对象头我们可以借助jol:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol‐core</artifactId>
<version>0.9</version>
</dependency>
public class A{}
public class JOLExample1 {
public static void main(String[] args) throws Exception {
out.println(VM.current().details());
out.println(ClassLayout.parseClass(A.class).toPrintable());
}
}
这样子输出后的结果如下:
# Running 64‐bit HotSpot VM.
# Using compressed oop with 0‐bit shift.
# Using compressed klass with 3‐bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
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) 82 22 01 20 (10000010 00100010 00000001 00100000) (536945282)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看出来'' (object header)"总共12byte ,也就是96bit,所以这里的指针被压缩过了;
分析一下这个(markword)64bit的意思:
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)
其中第一排括号里的前8bit表示: unsigned:0 ; age:0000 ; biased:0 ;lock:01
分别表示unsigned:没有使用,age:gc的分代年龄;biased:偏向锁的标志(0表示不是偏向锁,1表示是),lock:表示锁
这里的01表示无锁,00表示轻量锁,10表示重量锁;
下面这张图是32位的jvm:
64位的jvm和32的基本一致,后8位基本上是一样的,32位的时候无锁转态25bit表示hashcode值;
而64位是31bit表示hashcode值:如下图的括号里,从第一排第九个往下数31位表示hanscode值,剩下的25暂时没用到
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)
jvm启动时默认会使用偏向锁延迟(jvm估计认为,用synchronize的情况大多数都有竞争),延迟开启差不多在4s时间,当然可以配置jvm参数:
启用参数: -XX:+UseBiasedLocking 关闭延迟: -XX:BiasedLockingStartupDelay=0 禁用参数: -XX:-UseBiasedLocking
偏向锁怎么膨胀到轻量级锁或者到重量锁呢?
首先偏向锁(如果一个对象调用了,hashcode()方法,该对象就不可以是偏向锁了)是资源有且只有一个线程在竞争;(偏向一个线程后,基本上后面都会膨胀锁,不会再偏向另一个线程(批量重偏向除外))
偏向锁->轻量锁:多个线程,且是交替的执行,也就是一个线程获取到锁后,执行完同步块,这时候,另一个线程还处在自旋转态
去获取锁,且获取到了,总的来说是交替获取锁
偏向锁->重量锁:偏向锁还没执行完同步块代码,另一个线程自旋获取该锁,一直没获取到,挂起线程,这时候锁会升级重量锁;
(对象调用了wait()方法,则肯定会升级重量锁)
偏向锁在执行完同步代码块,锁不会撤销成无锁的情况;
轻量锁和重量锁在执行完同步代码块后,会执行锁撤销操作(挺耗性能毕竟要跟内核交互),变成无锁情况