范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

虚拟机中对象锁实现分析

  一、前言
  编程过程中经常会遇到线程的同步问题,Java 中对同步问题的解决方案比较多(synchronized、JUC、原子操作、volatile、条件变量等),其中synchronized 最方便、简单易用,也是java 编程中使用最多的临界区保护方案。本文主要讲述对象锁的相关知识,详细介绍synchronized 和Object 的关键方法的虚拟机实现原理。
  二、Java 对象锁的使用方式
  2.1 实例方法的同步
  synchronized 修饰实例方法,该同步仅对当前对象的该方法起作用,同一时间只能有一个线程可以进入该对象的此方法。对于不同对象的此函数,无法做到互斥保护。
  2.2 静态方法的同步
  synchronized 修饰静态方法,该同步对当前类对象的该方法起作用,同一时间只能有一个线程可以进入该方法。
  2.3 代码块的同步
  在大多少情况下,并不需要对整个方法进行保护,当synchronized 修饰代码块时,该代码块的访问依赖于object 对象锁的互斥访问,同一时间只能有一个线程持有object 对象锁。
  更准确的来讲,synchronized 关键字是依赖于对象锁而生效的,每个synchronized 同步块开始的地方都会生成monitor-enter obj指令,同步块结束的地方生成monitor-exit obj 的指令,其中obj 为用于控制互斥访问的对象。同一时间只能有一个线程持有obj 的对象锁。在2.1 中synchronized 依赖的是实列对象,2.2 中synchronized 依赖的是类对象,2.3 中synchronized 依赖的是object 对象。
  当一个对象控制多个代码块时,多个代码块也是互斥访问,如下面代码:
  代码块①和代码块② 虽然在两个函数中,但是synchronized 依赖的对象都为object,这两个代码块也是互斥访问。
  2.4 Object wait() 和notify() 使用方法
  Object 作为所有类的基类,都实现了object方法。典型的用法如下:
  thread 1持有object 对象锁,并调用object.wait() 方法后,则该线程进入WAITING状态,并释放object 对象锁,等待其它线程来唤醒它。
  当thread 2 持有object 对象锁,并调用object.notify()方法后,唤醒thread 1,thread 1
  重新获得object 对象锁继续执行。Object类方法说明:
  三、Android对象内存结构
  3.1 对象内存结构
  一个类的实例对象内存主要由3部分组成:
  1). 对象头:对象头包括kclass_和monitor_两个字段,其中kclass_ 存放指向类对象的指针,通过该指针可以找到该对象对应的类,monitor_ 用于存放对象运行时的标识数据,例如: GC 标志位、哈希码、锁状态等信息,后面详细分析。
  2). 实例数据,该部分存放实例变量值,父类实例变量值在前,子类在后,且实例变量值按照如下顺序进行排序:
  3).对齐填充,对象在内存中是按照8byte 对齐的,如果实例数据部分没有按照8byte对齐,则填充为8byte 对齐。
  3.2 monitor_ 字段分析
  monitor_ 字段定义在art/runtime/mirror/object.h,类型为uint32_t,主要有下面3个操作函数。
  操作函数中SetLockWord和CasLockWord函数的入参或GetLockWord函数的返回值都包含LockWord 变量,对monitor_ 字段的操作是通过LockWord 的值进行的。
  下面再来看LockWord 定义:
  LockWord 类的定义在art/runtime/lock_word.h 文件中,从注释中可以看到LockWord的使用主要有4种状态,如下:
  LockWord 的设计非常精妙,一个32 位数据的每一位都充分利用,而且很好的区分了不同状态。下面对各状态进行详细说明: unlocked/thin 状态下31-30 bit 为00,默认状态下为unlocked 状态,当对象进行线程同步时变成thin lock 状态,27-16bit 记录了thin lock重入的次数,15-0 bit 记录了持有该thin lock的线程ID。 fat lock状态下31-30 bit 为01,当对象锁在thin lock状态,且有新的(非owner)线程与其竞争,经过适当的等待期(sched_yield调用、循环获取thin lock 状态)后依然无法拿到锁,则转换为fat lock 状态,并为该对象分配一个Monitor 资源。 hash state状态下31-30 bit 为10,在27-0 bit 存储对象的hash code,当在其它模式下,hash code 会存储在该对象关联的Monitor 对象中。 forwarding address state 状态下31-30 bit 为11,在concurrent copying GC 的copy 阶段,当一个对象被拷贝后,指向拷贝后的对象地址,当线程访问到该对象后,通过该转发地址,访问新的对象。
  第29 位为mark bit,通过该bit位可以快速判断是否标记过,避免重复标记。
  第28 位为read barrier bit,如果对象LockWorkd的该bit 被设置,则在访问该对象的成员时会进入慢速路径,判断对象是不是需要更新,如果需要更新,则返回拷贝后的对象地址。
  四、对象锁代码分析
  4.1 首先我们看一段代码
  这段代码比较简单,主要有下面两个核心点:
  1). 在主线程执行的过程中,用obj 对象进行线程同步,并调用obj.wait()函数,使线程阻塞在了obj 对象锁上等待唤醒。
  2). main函数中创建匿名线程,该线程首先sleep 2000ms,然后唤醒阻塞在obj 对象锁上线程。
  4.2编译TestDemo.java,命令如下:
  1).Javac 将TestDemo.java 文件编译生成TestDemo*.class文件,java 编译过程中每个类会生成一个class 文件。
  2).d8 命令将TestDemo*.class 文件通过编译、重构、重排、压缩、混淆后生成对应的dex (Dalvik Executable file)格式文件。
  3).dexdump.exe命令可以查看dex 文件格式的详细信息,如校验信息、dex 头信息、生成dex 的CFG 信息、dex 的反汇编信息等,详细使用方法可以通过dexdump.exe –help 命令查看
  通过dexdump.exe –d classes.dex 查看反汇编
  其中run 方法指令信息如下:
  main 函数的指令信息如下:
  对部分指令解析如下:
  对于dex 指令详细格式可以阅读google 的官方文档:
  https://source.android.com/docs/core/runtime/dalvik-bytecode
  本文重点分析monitor-enter、monitor-exit、Object.wait()、Object.notify()在虚拟机中的详细实现。
  4.3.Object.wait() 流程分析
  Object.wait() 的调用关系如下:
  Object 类是所有类的父类,任何类中都可以调用public 的wait() 方法,最终调用到虚拟机的monitor.cc 文件的wait 静态方法,
  首先构造了一个操作obj 的Handle对象h_obj,通过ObjectWaitStart 函数通知jvmti 调试系统发生了JVMTI_EVENT_MONITOR_WAIT 事件。
  JVMTI(JVM Tool Interface)是 Java 虚拟机所提供的 native 编程接口,可以用来开发并监控虚拟机,可以查看JVM内部的状态,并控制JVM应用程序的执行。可实现的功能包括但不限于:调试、监控、线程分析、覆盖率分析工具等。
  首先获得h_obj 对象的LockWord 字段,lock_word.GetState()函数获得当前的锁状态,主要有下面几种情况:
  1).hash 或unlocked 状态:
  因为调用wait()方法必须持有对象锁,所以不会出现这两种状态,如果出现则抛出IllegalMonitorStateException 异常。
  2).thin lock 状态:
  当持有该对象锁的线程不是要wait 的线程,也抛出IllegalMonitorStateException 异常,当持有锁的线程与要wait 的线程一致,这时需要将thin lock inflate 为fat lock,inflate 的过程在monitor-enter 指令分析中分析。
  当对象锁inflate 为fat lock 状态后,调用Monitor 对象的实例方法Wait让线程进入sleep 状态等待。
  4.4 Object.notify() 流程分析
  这里我们直接分析DoNotify 函数:
  通过lock_word.GetState() 获得当前obj 对象的锁状态,主要有下面情况:
  1) hash 或unlocked 状态 :
  抛出IllegalMonitorStateException 异常。
  2).thin lock 状态:
  当持有该对象锁的线程不是要notify 的线程,也抛出IllegalMonitorStateException 异常,当持有锁的线程与要notify 的线程一致,这时说明没有需要通知唤醒的线程,直接返回。
  3).fat lock 状态:
  在Object.notify() 流程中参数notify_all 为false,则直接调用mon->Notify(self);通知唤醒等待线程。
  4.5monitor-enter 流程分析
  对于解释执行和机器码执行模式,最终都会调用到art/runtime/mirror/object-inl.h 文件Object 对象的MonitorEnter 函数。
  下面来分析Monitor类的静态方法MonitorEnter 函数。
  FakeLock 主要用于线程安全性检查,主要在编译期检测。
  kExtraSpinIters 定义了当对象锁被其它线程持有且为thin lock 时,竞争线程循环获取锁的次数。
  通过lock_word.GetState() 获取锁状态,当锁状态为unlocked 状态时,转换为thin lock 状态,并通过cas 操作更新lock count。
  当锁状态为thin lock 状态时,首先获取锁的owner 线程id,如果owner id 与竞争线程id 一致,则有下面两种情况: 如果lock count加1小于等于(1<<12)-1(4095)时,将lock count+1 更新lock count。 如果lock count加1大于(1<<12)-1时(lock count 区域无法存储),则调用InflateThinLocked 函数对thin lock 进行膨胀。
  Atrace* 相关的函数主要用于systrace 相关信息的打印,trylock 在这里为false。
  当锁状态为thin lock 状态且锁的owner 线程id 与竞争线程id 不一致,则做一定的等待。
  runtime->GetMaxSpinsBeforeThinLockInflation() 的值为50 ,也就是说执行100 次的循环判断锁状态后,再执行50次的sched_yield() 后还未获得锁资源,如果还未拿到锁,则对该锁进行膨胀。sched_yield() 会主动让出当前线程的执行权限,并在某个时间后恢复执行。
  当锁状态已经是fat lock 状态,通过lock_word.FatLockMonitor(); 获取Monitor 对象,并通过Monitor 对象的Lock 函数让线程进入等待状态。
  当锁状态已经是hash 状态时,直接对锁进行膨胀。
  下面看锁膨胀的过程:
  thin lock 的膨胀有两种情形:
  1).lock count 的值超过了4095,这时锁的owner 为当前线程,即直接通过Inflate 函数膨胀
  2).锁的owner不是当前线程,通过SuspendThreadByThreadId 暂停锁的owner 线程(主要是owner 线程和锁膨胀线程都需要访问对象的LockWord,避免竞态问题),然后通过Inflate 进行膨胀。膨胀完成后再唤醒锁的owner 线程。
  再看Inflate 的过程:
  通过MonitorPool::CreateMonitor函数获取一个Monitor 的对象m,并通过m->install(self)函数更新对象的LockWord字段,这时LockWord 字段信息包含fat lock 状态、GC 状态、MonitorId,然后将m 保存在monitor_list_ 中。
  monitor_list_中存储了当前虚拟机使用的所有Monitor 对象。在GC 的过程中,通过该链表,访问到Monitor 依赖的对象。如果对象变成垃圾对象,则回收该Monitor,否则更新Monitor 依赖的对象信息。
  MonitorId 用于唯一标识一个Monitor,生成的方法可以看monitor_pool.h 中的实现。
  再看Monitor::Lock的过程:
  该函数的实现较长,省去调试相关的代码。
  首先介绍Monitor 中最重要的成员monitor_lock_ ,它是Mutex 的实例,通过该实例实现锁相关的核心逻辑。
  TryLock 函数主要是通过Mutex的函数实现一定的自旋等待,并设置锁的状态为线程持有的状态。
  monitor_lock_.ExclusiveLock(self);在Mutex 的ExclusiveLock函数中通过futex 系统调用实现了线程的阻塞,futex调用代码如下。
  4.6 monitor-exit 流程分析
  解释执行和机器码执行模式都会调用到MonitorExit 函数。
  通过lock_word.GetState()获取LockWord 状态,当状态为hash 或unlocked 状态时,通过FailedUnlock函数抛出异常。
  当LockWord 的状态为thin lock 状态时,有下面两种情况:
  1).锁的owner 与当前线程不一致,则出错抛出异常。
  2).锁的owner 与当前线程为同一线程,当锁有重入时,则将lock count -1,否则设置为unlocked 状态。
  当LockWord 的状态为fat lock状态时,获取该对象关联的Monitor 对象,并调用Unlock 函数
  在Unlock 函数中lock_count 为0,说明该线程不在持有该锁,通过
  SignalWaiterAndReleaseMonitorLock 唤醒阻塞在该锁上的线程。
  五、总结
  本文简单的阐述了对象锁的使用方式,对象在内存中的结构,并对对象头中关键成员LockWord 进行了分析,最后介绍了synchronized、Object.wait()和Object.notify()在虚拟机中的实现流程。
  参考文献:
  深入理解Android Java虚拟机ART 邓凡平
  深入理解java 虚拟机 周志明
  深入java 虚拟机 bill venners
  Android T 源码

智微智能最新公告公司及子公司拟使用合计不超18亿元闲置募资及自有资金进行现金管理智微智能公告,在确保不影响公司正常运营和募集资金投资项目建设的情况下,公司及子公司拟使用额度不超过人民币8亿元的闲置募集资金和不超过10亿元的闲置自有资金进行现金管理。截至2022海南证监局上半年辖区上市公司亏损幅度进一步加大海南证监局上半年辖区上市公司亏损幅度进一步加大财联社9月15日电,今日举行的海南辖区投资者网上集体接待日活动上,中国证监会海南监管局党委委员副局长罗精晖表示,去年海南上市公司累计现1264家中国上市公司积极参与共建一带一路东南亚成业务首选地中国上市公司参与共建一带一路收获累累硕果。近日,中国上市公司协会发布了中国上市公司共建一带一路白皮书2021(简称白皮书),展示了近年来中国上市公司参与一带一路的成绩单。据悉,20碳酸饮料好处多多,你知道哪些?1补充水分,碳酸饮料的最主要成分是水,饮用后可补充身体因运动和进行生命活动所消耗掉的水分和一部分糖矿物质,对维持体内的水液电解质平衡有一定作用。2碳酸饮料因含有二氧化碳,所以能起到美网开赛小威开启职业生涯谢幕战,退役并不影响商业价值作者杨弋北京时间2022年8月29日,2022美国网球公开赛正赛打响。美国著名女子网球运动员塞雷娜威廉姆斯将在8月30日上午迎来首轮较量,对手是来自黑山的非种子选手科维尼奇。对于即官宣!中国女篮双塔正式归队率队迎战5大强敌PK美国队备受期待中国女篮接下来最主要的任务就是世界杯的争夺,本届女篮世界杯共有12支球队进入到正赛,根据国际篮联的最新公布,中国队的综合实力排名第二位仅次于美国队,因此本届世界杯中国女篮完全有希望打完巴林天亮了!杜锋不再发愁!3个月后张镇麟和胡金秋补强锋线2022年对于杜锋和中国男篮来说,是真正考验2019年之后国家队成色的一年。2019年世界杯上,中国男篮在家门口输给了伊朗,丢掉了直通东京奥运会的门票,接近着又在奥运落选赛中两战皆(外代二线)马德里时装周MarcosLuengo品牌时装秀(外代二线)马德里时装周MarcosLuengo品牌时装秀9月12日,模特在西班牙马德里时装周上展示MarcosLuengo品牌的新款服装。新华社欧新9月12日,模特在西班牙马德里从3连败到4连胜!瓜迪奥拉弟子神了,英超全胜球队诞生,剑指冠军202223赛季英超联赛继续进行,新赛季开启后,各大豪门球队都或多或少地失分,但让球迷刮目相看的是曾经的争四狂魔枪手阿森纳,阿尔特塔领衔的年青枪手上赛季英超开局遭遇3连败,但经过今男篮世界杯预选赛中国对巴林,差点翻船,下一轮对手是伊朗男篮世界杯预赛,是成长了?还是对手太弱?开端就打出2511的领先优势,有侵略性,敢冲敢投了,但是弱点太明显了,还差点阴沟里翻船,让我们球迷看得很着急,无论是篮板的稳定上,还是心态上一个醉酒女孩被侵犯之后,醒来之后发现自己记不清发生了什么咱们在这个世界有时候总是有一些免不掉的应酬,真的是身不由己!俗话说得好,人在江湖飘,哪有不挨刀!我们平时在工作的时候或者交际的时候,总是难以避免遇到一些酒局或者饭局,拒绝一两次还可
欢瑞祥和过大年锣鼓声声庆丰年咚咚!咚咚锵!1月24日,大年初三的清晨,在冬日暖阳下的安庆市宜秀区罗岭镇凤溪村祠堂组,村民们穿戴喜庆的服饰,带着各自的家伙,聚集在村口的广场上,一场锣鼓表演正火热上演。这两年家乡同样是篡位登基,李世民的负面评价为何比朱棣少?不会宠文人李世民是夺嫡,朱棣是明目张胆地篡位李世民文武全才,确实有本事,自己既能马上取天下,还能下马服臣民,朱棣武的一般,文的更差劲李世民自信源于自身的势力,朱棣残暴猜忌因于自卑基因遗传。朱新春走基层欢乐祥和过大年绿美云南引客来丽江机场人流如织。通讯员曹凌燕刘爱华摄春节假期,出门旅游成为很多人首选的度假方式。凭借自然风光生态环境和气候温暖舒适等优势,云南旅游市场持续升温。据携程美团等机构统计数据显示,假日老山之战的未解之谜为何将14军和40师指挥所定在干田村凹地?脖子上挂光荣弹,手拿冲锋枪,背着塑料水桶的老山骆驼军工战士往山上送水在1984年4月28日进行的收复老山之战中,有两个未解之谜第一个是40师118团1营在穿插途中落入越军预设炮火打阿登战役中德军装甲兵团竟是缩水部队?党卫军精锐也没有余粮!1944年12月,第三帝国的寿命只剩下最后五个月时间了,但日益偏执癫狂的希特勒依然相信自己有逆风翻盘的机会,将希望寄托在西线的一次绝地反攻之上,并选择了1940年西欧战役中出奇制胜巴铁应美国要求,将159集装箱炮弹送往乌克兰,这给中国提了个醒应美国要求,巴铁将159集装箱军火运往乌克兰,这给中方提了个醒。此次运输的弹药都是巴基斯坦自行生产的,虽然没有透露具体数字,但159个集装箱预计超过30万枚。巴基斯坦此前也在国际上我,双一流毕业,为老公放弃事业随军当军嫂,在部队带娃月入过万这是我们讲述的第1464位真人故事我是暖暖暖暖军恋,今年28岁,江西人。我的爱人是我的小学同桌,我们高二开始恋爱。大学毕业后,我成了电视台主持人,他做了军官。经过6年异地恋,我放弃喜爱VirgilAbloh者,别忘了他这双布满人造皮草的Nike波鞋VirgilAbloh虽已离世一年多了,但他的设计所创下的标竿,即使在未来,依然会屹立不搖,其设计的单品中最经典者莫过于运动鞋,不论是TheTen,还是和Nike合作的一系列Air熬夜人这样护肤才不会老!估计这几天姐妹们多多少少有点节后综合征眩晕焦虑肠胃不适整个人都没精神,很大一部分原因是假期熬夜。春节一旦得了闲,谁不想来几次报复性熬夜,能有多大胆,就敢睡多晚。然而熬夜带来的杀伤力这10双球鞋现在抄底正合适!倒钩一千多就能买!小孩子拿到了压岁钱,大人们拿到了年终奖。距离除夕过去一周,相信大家早就盘算着买双新鞋了。正因为各位现在手里都宽裕。所以这份清单不仅有原价左右的AirJordan,也有价格不菲,但错阳了能敷面膜吗?为何皮肤会变好?6个护肤问题一篇解答前段时间,一则女子发烧敷20分钟面膜揭下变3D立体的消息引发了网友热议。有网友神评论到别人敷面膜是保养,你这是大火收汁。还有网友感叹即使阳了,也要美丽!调侃归调侃,但新冠阳性以后到