一个java对象包含:对象头,数据,对齐填充;
对象头包含:markword(如上图),类类型指针(klass word,如上图),legth(若是数组对象有这个值)
现在讲解下上图:
上图是jvm64位的对象头在各种锁状态下的信息;正常情况markword占64bit ;klass word 占64bit(一般默认开启指针压缩的:会压缩到32bit)
要分析对象头我们可以借助jol:
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()方法,则肯定会升级重量锁)
偏向锁在执行完同步代码块,锁不会撤销成无锁的情况;
轻量锁和重量锁在执行完同步代码块后,会执行锁撤销操作(挺耗性能毕竟要跟内核交互),变成无锁情况;
public static void main(String[] args) throws Exception {
Thread.sleep(5000);
final List listA = new ArrayList();
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++)
{
A a = new A();
synchronized (a) {
listA.add(a);
}
}
try
{
Thread.sleep(100000000);
} catch (
InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(3000);
Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 40; i++) {
A a = listA.get(i);
synchronized (a) {
}
}
try {
Thread.sleep(20000);
A a = listA.get(88);
synchronized (a) {
out.println("打印list中第89个对象的对象头:");
out.println((ClassLayout.parseInstance(a).toPrintable()));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t2.start();
Thread.sleep(3000);
out.println("打印list中第11个对象的对象头:");
out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable()));
out.println("打印list中第26个对象的对象头:");
out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable()));
out.println("打印list中第90个对象的对象头:");
out.println((ClassLayout.parseInstance(listA.get(89)).toPrintable()));
out.println("=============重新输出新实例1");
out.println((ClassLayout.parseInstance(new A()).toPrintable()));
Thread t3 = new Thread() {
public void run() {
for (int i = 20; i < 60; i++) {
A a = listA.get(i);
synchronized (a) {
if (i == 20 || i == 40 ||i == 52) {
out.println("thread3 第" + i + "次");
out.println((ClassLayout.parseInstance(a).toPrintable()));
}
}
}
}
};
t3.start();
Thread.sleep(2000);
out.println("重新输出线程1的状态--------------");
out.println((ClassLayout.parseInstance(listA.get(98)).toPrintable()));
Thread.sleep(2000);
out.println("重新输出新实例2");
out.println((ClassLayout.parseInstance(new A()).toPrintable()));
}
A类就是一个普通的类,什么都没有,因为,jvm启用偏向锁延迟在4s左右,所以这里的demo没有设置参数,而是直接睡5s中,接下来看输出结果:
打印list中第11个对象的对象头:
com.luban.layout.A 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) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
打印list中第26个对象的对象头:
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 19 64 1a (00000101 00011001 01100100 00011010) (442767621)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
打印list中第90个对象的对象头:
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 64 1a (00000101 00000000 01100100 00011010) (442761221)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=============重新输出新实例1
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 01 00 00 (00000101 00000001 00000000 00000000) (261)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
thread3 第20次
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 90 f3 d9 1b (10010000 11110011 11011001 00011011) (467268496)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
thread3 第40次
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 90 f3 d9 1b (10010000 11110011 11011001 00011011) (467268496)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
thread3 第52次
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 90 f3 d9 1b (10010000 11110011 11011001 00011011) (467268496)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
重新输出线程1的状态--------------
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 64 1a (00000101 00000000 01100100 00011010) (442761221)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
重新输出新实例2
com.luban.layout.A 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) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
打印list中第89个对象的对象头:
com.luban.layout.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) a8 ee 47 1b (10101000 11101110 01000111 00011011) (457698984)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 62 c2 00 20 (01100010 11000010 00000000 00100000) (536920674)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
首先介绍两个阈值:
intx BiasedLockingBulkRebiasThreshold = 20 默认偏向锁批量重偏向阈值
intx BiasedLockingBulkRevokeThreshold = 40 默认偏向锁批量撤销阈值
第一个阔值是重偏向:但我们创建一个类的时候这里是A.class .在其class会保存一个epoch值,假设是00,
1,new的每个对象它的对象头的epoch值,和class的一样,这例子中,先new了100个对象A.此时100个对象的线程都是偏向锁.
2,线程2去重写获取锁,这时候只有它本身竞争,所以这时候它会先进性(看该对象的epoch的值是否与对象的class里的epoch值是否相等,不相等的话,直接覆盖原有的偏向锁)cas操作(期望该对象头是没有锁的,若没有则,直接调用os的cas函数进行替换,cas函数在os系统中是一个指令所以是安全的), 例子中是该对象有线程1偏向锁,所以cas失败,这时候,就会进行锁撤销,撤销一次A.class的值会记录一次,当达到20次的时候,会改变class的epoch值变成01.
3.例子中线程2是循环拿了40个对象,这样前20会进行批量撤销操作,重新加锁为轻量锁,当该锁执行完同步块,会变成无所状态,20-40发生了重偏向所以其还是偏向锁只不过是另一个线程(这里的线程可能还是一样是因为jvm的线程复用情况,一种猜测),
4,所以打印的时候11输出的是无锁是因为,它本是轻量锁,退出了代码块会变成无锁,后面的26和90都是偏向锁,然后在打印一个重新new的对象(打印里的重新输出实例1),默认是偏向锁的状态,只是此时的锁不偏向任何线程.
5,当线程2执行完进入睡眠,这时候线程3执行,此时只有3竞争锁,所以它跟线程2一样进行上面第二部操作,当锁撤销到40次的时候,jvm会把该class认为是有问题的可能,这时候,该class创建的实力对象永远不可能是偏向锁了((打印里的重新输出实例2)),且重偏向也会无效了(打印里的第89个对象头);
之前介绍了无锁的对象头和偏向锁的对象头的不同,接下来看轻量锁和重量锁
轻量锁的对象头,若是jvm32位的前30位保存lock_recod指针,若是64位前62保存指针,指向该持有线程的栈中(后续介绍)
重量锁的对象头,若是jvm32位的前30位保存指向堆的指针,若是64位前62保存指针,实现是用monitor函数(jvm的源码)(后续介绍)