当前位置: 首页 > 科技观察

MarkWord

时间:2023-03-12 05:58:58 科技观察

,面试官很喜欢问的,就是年底了。我最近几天没吃东西。微博吃瓜吃饱了。面试官在上次被问到synchronizedlock之后,继续刁难阿巴,然后在objectheader中深入到相关概念。当场接受报价。面试官:上次提到了synchronized锁。你知道synchronized锁是怎么实现的吗?阿巴阿巴:在JDK1.5版本及更早版本中,synchronized主要依靠Monitor对象来完成,同步代码块使用monitorenter和monitorexit指令,synchronized修改方式依赖于ACC_SYNCHRONIZED标志。这些都是在内核态加锁的,然后竞争锁失败的线程直接挂起,等待后面回收。阿爸阿爸:在JDK1.6及以后的版本中,对synchronized锁进行了优化,引入了自适应自旋锁、偏向锁、轻量级锁。它们主要是在一定条件下优化锁的性能。避免一上来就加重量级锁,其他等待锁的线程只能乖乖挂掉,对CPU性能影响特别大。阿爸阿爸:热点虚拟机中,对象头主要包括两部分,MarkWord和KlassPointer。MarkWord对象标记字段,默认存储对象的HashCode、GC的分代年龄(2位最大值为15)、锁的标志信息等。对于32位虚拟机,MarkWord占用32位,对于64位虚拟机来说,MarkWord占用64字节。KlassPointerClass对象的类型指针,指向该对象对应的Class对象的内存地址。大小占用4个字节(指针压缩情况下为4字节,无指针压缩情况下为8字节)。32位虚拟机MarkWord分布64位虚拟机MarkWord分布图片来源https://blog.csdn.net/weixin_40816843/article/details/120811181查看有多少个虚拟机:java-version面试官:我们怎么看对象头中的MarkWord数据呢?阿爸阿爸:你可以在openJDK中看到MarkWord的说明。首先,您可以在Github上找到OpenJdk的源代码。gitHub地址:https://github.com/openjdk/jdk在IDE中打开找到如下位置src/hotspot/share/oops/markWord.hpp//查看虚拟机可用的位数:java-version//32bits://--------//hash:25------------>|age:4unused_gap:1lock:2(normalobject)////64bits://--------//unused:25hash:31-->|unused_gap:1age:4unused_gap:1lock:2(normalobject)阿巴阿巴:当然可以导入openjdk提供的jol-core,和然后打印出来。//在pom中引入org.openjdk.joljol-core0.10并写入如下代码publicstaticvoidmain(String[]args){Testt=newTest();System.out.println(ClassLayout.parseInstance(t).toPrintable());}打印如下标记在哪里?Klass指针在哪里?1位是MarkWord占8Byte也就是64bit2重点是KlassPointer占4Byte,也就是32bit的klass指针好像被压缩了。你怎么能确定它是压缩的?面试官可以使用如下命令:对于JDK1.6及以上版本,synchronized和MarkWord有什么关系?Baaba:这与它有很大关系。可以看到MarkWord中有一个2位的flag用来表示锁,意思是优化后的synchronized锁不会直接锁重量级锁,而是从偏向锁转为轻量级锁。锁,然后从轻量级锁到重量级锁,一步步扩大的过程。下面是2位锁标志的含义//[ptr|00]lockedptrpointstorealheaderonstack//[header|01]unlockedregularobjectheader//[ptr|10]monitorinflatedlock(headeriswappedout)//[ptr|11]markedusedtomarkanoobject//[0。..........0|00]inflatinginflationinprogress001无锁状态(第一位代表偏向标志,为0时表示无偏向,为1时表示偏向)101偏向锁andrecordthreadID00light量化锁指向栈中锁记录的指针10重量级锁重量级锁的指针11GCflag然后找到上图Value部分的数据,这两个就是锁的flags面试官:你刚才不是说其中一个锁有偏置标志吗?它在哪里?阿巴:锁的偏置标志就在锁标志之前。Ababa:程序启动后4s会加一个偏向锁,但是这个偏向锁不偏向任何线程ID,也属于无锁状态AbabaAbaba:当应用处于单线程环境时,一个偏向此时加锁,偏向标记在对象头中显示为1。案例如下}).start();}打印数据如下阿巴:让程序处于2个线程交替竞争锁publicstaticvoidmain(String[]args)throwsInterruptedException{Testt=newTest();Threadthread=newThread(()->{synchronized(t){System.out.println(ClassLayout.parseInstance(t).toPrintable());}});thread.start();//等待线程完成运行thread.join();同步(t){System.out.println(ClassLayout.parseInstance(t).toPrintable());}}可以看出,当主线程拿到锁的时候,扩展成了一个轻量级的锁,锁的2bitflag变成了00。创建锁记录的机会当前线程栈帧中的“LockRecord”空间用于存放锁对象当前MarkWord的副本。此步骤使用CAS。如果成功,那么与此同时,2位锁标记位将从“01”变为“00”,就是添加轻量级锁的过程。阿爸阿爸:之所以引入偏向锁,是为了在没有多线程竞争的环境下解决轻量级锁。轻量级锁CAS的多次尝试也是性能上的损失。与轻量级锁相比,偏向锁值只需要一次CAS。此CAS用于设置线程ID。设置成功后,表示已经获取到了锁。轻量级锁更适合线程交替执行的场景。他们使用CAS自旋来避免线程的直接挂起和挂起后的恢复过程,从而减少CPU的消耗。阿巴阿巴:最后我们来看看加了权重锁后的MarkWord表现。一、代码;}});thread.start();//等待线程运行完毕//thread.join();去掉代码synchronized(t){System.out.println(ClassLayout.parseInstance(t).toPrintable());}}控制台打印如下,发现加了权重锁,2bit标记锁的位变成了10。Abba阿巴:当轻量级锁升级为重量级锁时,MarkWord的锁标志位更新为10,MarkWord会指向mutex(重量级锁)。阿爸阿爸:以上是关于synchronized和MarkWord的关系。面试官:明白了,明天来上班~阿爸阿爸:好的~