springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)java
java开源商城系统git
jdk版本:1.8spring
系统:window10 64位数组
jvm 启动参数:-XX:BiasedLockingStartupDelay=0 (取消延迟加载偏向锁)springboot
首先须要已知几个概念jvm
若是是 array 对象,则会再占用一个 length 空间(4 字节),记录数组的长度。工具
3.synchronized 四锁升级流程以及什么时候升级(不赘述)编码
经过实际编码查看分别在这四个锁状态时,锁标志位是否相应变化spa
引入 jol 工具包线程
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
无锁态
@Test public void test01() throws Exception { Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
执行结果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
第1、二行的 object header 是 markword 的内存,第三行的 object header 是 class pointer 的内容,第四行是对齐填充。
只需关注第1、二行。
第1、二行由于是由低位打印到高位的,因此须要反过来看才会和上方的锁状态表格中一一对应。
即 <u>00000000 00000000 00000000 00000000 00000000 00000000 000000</u>00 00000101。
对照表格,重点最后三位,是否偏向锁为1,锁标志位为01,理论上来讲偏向锁标记应该为0,这是由于咱们加了个 取消延迟加载偏向锁的启动参数致使的,若是把启动参数去掉,那么偏向锁标志位就是0。
JVM启动时会进行一系列的复杂活动,好比装载配置,系统类初始化等等。在这个过程当中会使用大量synchronized关键字对对象加锁,且这些锁大多数都不是偏向锁。为了减小初始化时间,JVM默认延时加载偏向锁,而咱们把它禁用掉了,因此偏向锁标志位就变成了 1。
即使如此,依旧能够看出下划线部分为偏向线程的 id 存储位置,目前全为 0,也即这个对象目前不偏向任何线程,因此当前对象仍是可偏向的状态
即当前对象锁状态为无锁态得证。
偏向锁态
@Test public void test2() throws Exception { Object o = new Object(); synchronized (o) {} System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
执行结果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 e8 d1 02 (00000101 11101000 11010001 00000010) (47310853) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
同样先把前两行按表格中的顺序先排列好,以下:
<u>00000000 00000000 00000000 00000000 00000010 11010001 111010</u>00 00000101
这回能够看出偏向锁标志位1,锁标志位为01,并且偏向锁的线程 id 也被占用了,因此显然该对象线程是偏向锁态。
同时,从代码中看出,是先对 o 上 synchronized 锁且锁释放以后才对 o 进行打印的,能够得出一个结论,偏向锁状态不会主动撤销,而是会继续保留其状态。
轻量级锁
@Test public void test3() throws Exception { Object o = new Object(); synchronized (o) {} new Thread(() -> { synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }).start(); TimeUnit.SECONDS.sleep(10); }
执行结果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 00 ef a5 1e (00000000 11101111 10100101 00011110) (514191104) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
00000000 00000000 00000000 00000000 00011110 10100101 11101111 00000000
对照表,显然此时的 o 处于轻量级锁态。由于主线程先对 o 上锁,o处于偏向锁,而后再来个线程对 o 上锁,上锁前就偏向锁就会膨胀为轻量级锁。
综上,得证。
重量级锁
@Test public void test4() throws Exception { Object o = new Object(); AtomicInteger index = new AtomicInteger(0); for (int i = 0; i < 10; i++) { new Thread(() -> { synchronized (o) { index.getAndIncrement(); if(index.get() == 9) System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }).start(); } TimeUnit.SECONDS.sleep(10); }
执行结果
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) fa 05 d7 1c (11111010 00000101 11010111 00011100) (483853818) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
00000000 00000000 00000000 00000000 00011100 11010111 00000101 11111010
出现大量锁竞争,理论上 o 应当处于重量级锁态,对照表格显然得出是 o 处于重量级锁态,得证。
再来看以下两种状况
在对 o 上锁前计算 o 的 hashcode,上锁时 o 处于什么状态
@Test public void test5() throws Exception { Object o = new Object(); o.hashCode(); synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }
从理论上将应该仍是偏向锁,那么看执行结果。
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) b8 e4 ac 02 (10111000 11100100 10101100 00000010) (44885176) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
此时的锁标志位为 00,为轻量级锁,并非偏向锁。
结论:计算过 hashcode 的对象,在上锁后会直接膨胀为轻量级锁,跳过偏向锁。
在对 o 上锁后计算 o 的hashcode,此时的 o 处于什么状态
@Test public void test6() throws Exception { Object o = new Object(); synchronized (o) { o.hashCode(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }
理论上这里的 o 应该是偏向锁。看执行结果:
java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) ba c7 52 1c (10111010 11000111 01010010 00011100) (475187130) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
此时的锁标志位为 10,为重量级锁,并非偏向锁。
结论:处于偏向锁态的对象只要计算过 hashcode,会直接膨胀为重量级锁。
拓展:
检验代码中计算出来的 hashcode 与 markword 中对应记录 hashcode 中的值一致
@Test public void test7() throws Exception { Object o = new Object(); System.out.println(o.hashCode()); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }
输出结果:
1174290147 java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 e3 3e fe (00000001 11100011 00111110 11111110) (-29433087) 4 4 (object header) 45 00 00 00 (01000101 00000000 00000000 00000000) (69) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
将 markword 抄写出来,即 00000000 00000000 00000000 01000101 11111110 00111110 11100011 00000001
对照文章开头的表格,可知从第26位开始到底56位都是记录 hashcode 的比特位。即 1000101 11111110 00111110 11100011,将其转为十进制,结果为 1,174,290,147。和代码中输出的结果一致。