ART世界探险(9) - 同步锁
来源:互联网 发布:淘宝小钱包 编辑:程序博客网 时间:2024/04/28 14:38
ART世界探险(9) - 同步锁
Java是一种把同步锁写进语言和指令集的语言。
从语言层面,Java提供了synchronized关键字。
从指令集层面,Java提供了monitorenter和monitorexit两条指令。
下面我们就看看它们是如何实现的吧。
三种锁的方式
Java代码
有三种方式来加锁:
* 直接在函数上加synchronized关键字
* 在函数内用某Object去做同步
* 调用concurrent库中的其他工具
public synchronized int newID(){ return mID++; } public int newID2(){ synchronized (mObj){ mID++; } return mID; } public int newID3(){ Lock lock = new ReentrantLock(); try{ lock.lock(); mID++; }finally { lock.unlock(); } return mID++; }
Class字节码
第1个由于是加在函数上的属性,所以对字节码没有造成任何影响。
public synchronized int newID(); Code: 0: aload_0 1: dup 2: getfield #2 // Field mID:I 5: dup_x1 6: iconst_1 7: iadd 8: putfield #2 // Field mID:I 11: ireturn
第二个,就会生成对应的monitorenter和monitorexit指令。
public int newID2(); Code: 0: aload_0 1: getfield #4 // Field mObj:Ljava/lang/Object; 4: dup 5: astore_1 6: monitorenter 7: aload_0 8: dup 9: getfield #2 // Field mID:I 12: iconst_1 13: iadd 14: putfield #2 // Field mID:I 17: aload_1 18: monitorexit 19: goto 27 22: astore_2 23: aload_1 24: monitorexit 25: aload_2 26: athrow 27: aload_0 28: getfield #2 // Field mID:I 31: ireturn Exception table: from to target type 7 19 22 any 22 25 22 any
第三个,由于是新的工具,从指令集上是得不到支持的。
顺带我们讲一下try和finally,末尾有一个异常表,从第8号到24号指令是其范围,出现异常会跳到33语句处。
public int newID3(); Code: 0: new #7 // class java/util/concurrent/locks/ReentrantLock 3: dup 4: invokespecial #8 // Method java/util/concurrent/locks/ReentrantLock."<init>":()V 7: astore_1 8: aload_1 9: invokeinterface #9, 1 // InterfaceMethod java/util/concurrent/locks/Lock.lock:()V 14: aload_0 15: dup 16: getfield #2 // Field mID:I 19: iconst_1 20: iadd 21: putfield #2 // Field mID:I 24: aload_1 25: invokeinterface #10, 1 // InterfaceMethod java/util/concurrent/locks/Lock.unlock:()V 30: goto 42 33: astore_2 34: aload_1 35: invokeinterface #10, 1 // InterfaceMethod java/util/concurrent/locks/Lock.unlock:()V 40: aload_2 41: athrow 42: aload_0 43: dup 44: getfield #2 // Field mID:I 47: dup_x1 48: iconst_1 49: iadd 50: putfield #2 // Field mID:I 53: ireturn Exception table: from to target type 8 24 33 any
Dalvik代码
我们首先看看第一个,翻译成Dalvik指令后发生了变化,增加了monitor-enter和monitor-exit指令来包围这个方法。
从中可以看到,即使发生了exception,也是能正常走到monitor-exit的。
#2 : (in Lcom/yunos/xulun/testcppjni2/SampleClass;) name : 'newID' type : '()I' access : 0x20001 (PUBLIC DECLARED_SYNCHRONIZED) code - registers : 3 ins : 1 outs : 0 insns size : 12 16-bit code units1328c4: |[1328c4] com.yunos.xulun.testcppjni2.SampleClass.newID:()I1328d4: 1d02 |0000: monitor-enter v21328d6: 5220 7a1d |0001: iget v0, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mID:I // field@1d7a1328da: d801 0001 |0003: add-int/lit8 v1, v0, #int 1 // #011328de: 5921 7a1d |0005: iput v1, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mID:I // field@1d7a1328e2: 1e02 |0007: monitor-exit v21328e4: 0f00 |0008: return v01328e6: 0d00 |0009: move-exception v01328e8: 1e02 |000a: monitor-exit v21328ea: 2700 |000b: throw v0
第二个:
这是我们手工加的,除了用Object之外,跟上一个已经区别不大了。
#3 : (in Lcom/yunos/xulun/testcppjni2/SampleClass;) name : 'newID2' type : '()I' access : 0x0001 (PUBLIC) code - registers : 3 ins : 1 outs : 0 insns size : 16 16-bit code units1328f8: |[1328f8] com.yunos.xulun.testcppjni2.SampleClass.newID2:()I132908: 5421 7b1d |0000: iget-object v1, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mObj:Ljava/lang/Object; // field@1d7b13290c: 1d01 |0002: monitor-enter v113290e: 5220 7a1d |0003: iget v0, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mID:I // field@1d7a132912: d800 0001 |0005: add-int/lit8 v0, v0, #int 1 // #01132916: 5920 7a1d |0007: iput v0, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mID:I // field@1d7a13291a: 1e01 |0009: monitor-exit v113291c: 5220 7a1d |000a: iget v0, v2, Lcom/yunos/xulun/testcppjni2/SampleClass;.mID:I // field@1d7a132920: 0f00 |000c: return v0132922: 0d00 |000d: move-exception v0132924: 1e01 |000e: monitor-exit v1132926: 2700 |000f: throw v0 catches : 2 0x0003 - 0x000a <any> -> 0x000d 0x000e - 0x000f <any> -> 0x000d positions : 0x0000 line=27 0x0003 line=28 0x0009 line=29 0x000a line=30 0x000d line=29 locals : 0x0000 - 0x0010 reg=2 this Lcom/yunos/xulun/testcppjni2/SampleClass;
第三个就像普通函数调用,就不多说了。
OAT的生成代码
因为前两个结构非常相似,到OAT这一级,我们就只分析第一个。
3: int com.yunos.xulun.testcppjni2.SampleClass.newID() (dex_method_idx=16780) DEX CODE: 0x0000: 1d02 | monitor-enter v2 0x0001: 5220 7a1d | iget v0, v2, I com.yunos.xulun.testcppjni2.SampleClass.mID // field@7546 0x0003: d801 0001 | add-int/lit8 v1, v0, #+1 0x0005: 5921 7a1d | iput v1, v2, I com.yunos.xulun.testcppjni2.SampleClass.mID // field@7546 0x0007: 1e02 | monitor-exit v2 0x0008: 0f00 | return v0 0x0009: 0d00 | move-exception v0 0x000a: 1e02 | monitor-exit v2 0x000b: 2700 | throw v0 OatMethodOffsets (offset=0x00277794) code_offset: 0x0066275c gc_map: (offset=0x002ce242) OatQuickMethodHeader (offset=0x00662740) mapping_table: (offset=0x0030855a) vmap_table: (offset=0x0030df6a) v65535/r30 QuickMethodFrameInfo frame_size_in_bytes: 32 core_spill_mask: 0x40000000 (r30) fp_spill_mask: 0x00000000 vr_stack_locations: locals: v0[sp + #12] v1[sp + #16] ins: v2[sp + #40] method*: v3[sp + #0] CODE: (code_offset=0x0066275c size_offset=0x00662758 size=168)... 0x0066275c: d1400bf0 sub x16, sp, #0x2000 (8192) 0x00662760: b940021f ldr wzr, [x16] suspend point dex PC: 0x0000 GC map objects: v2 ([sp + #40])
前面还是不变的判suspend.
w1存的是当年的对象,存到sp+40,这个下面就当成锁对象用。
0x00662764: f81e0fe0 str x0, [sp, #-32]! 0x00662768: f9000ffe str lr, [sp, #24] 0x0066276c: b9002be1 str w1, [sp, #40] 0x00662770: 79400250 ldrh w16, [tr] (state_and_flags) 0x00662774: 35000430 cbnz w16, #+0x84 (addr 0x6627f8)
把刚从sp+40的当前对象取出来到w0。
然后调用pLockObject过程去加锁。
0x00662778: b9402be0 ldr w0, [sp, #40] 0x0066277c: f9419e5e ldr lr, [tr, #824] (pLockObject) 0x00662780: d63f03c0 blr lr suspend point dex PC: 0x0000 GC map objects: v2 ([sp + #40])
w0的引用对象再读回来。
sp+12是域变量mID,读取寄存器w1
然后再存回去,再读到w0中,因为下一个加法要在w0中算。
0x00662784: b9402be0 ldr w0, [sp, #40] 0x00662788: b940001f ldr wzr, [x0] suspend point dex PC: 0x0001 GC map objects: v2 ([sp + #40]) 0x0066278c: b9400c01 ldr w1, [x0, #12] suspend point dex PC: 0x0001 GC map objects: v2 ([sp + #40]) 0x00662790: b9000fe1 str w1, [sp, #12] 0x00662794: b9400fe0 ldr w0, [sp, #12]
计算mID++,存到sp+16中。
再把对象从sp+40再读出来,再找到mID,存回去到sp+12.
0x00662798: 11000401 add w1, w0, #0x1 (1) 0x0066279c: b90013e1 str w1, [sp, #16] 0x006627a0: b9402be0 ldr w0, [sp, #40] 0x006627a4: b940001f ldr wzr, [x0] suspend point dex PC: 0x0005 GC map objects: v2 ([sp + #40]) 0x006627a8: b9000be0 str w0, [sp, #8] 0x006627ac: b9400be0 ldr w0, [sp, #8] 0x006627b0: b94013e1 ldr w1, [sp, #16] 0x006627b4: b9000c01 str w1, [x0, #12]
解锁,从sp+40把对象引用再读出来,用这个对象做参数调pUnlockObject.
0x006627b8: b9402be0 ldr w0, [sp, #40] 0x006627bc: f941a25e ldr lr, [tr, #832] (pUnlockObject) 0x006627c0: d63f03c0 blr lr suspend point dex PC: 0x0007 GC map objects: v2 ([sp + #40])
sp+12记得是mID变量哈,读到w0,准备返回,恢复LR,清理栈,返回。
0x006627c4: b9400fe0 ldr w0, [sp, #12] 0x006627c8: f9400ffe ldr lr, [sp, #24] 0x006627cc: 910083ff add sp, sp, #0x20 (32) 0x006627d0: d65f03c0 ret
下面是exception情况下的解锁,将exception的值存在sp+12中。
从sp+40读对象引用,用它去pUnlockObject解锁。
catch entry dex PC: 0x0009 0x006627d4: b9408a40 ldr w0, [tr, #136] (exception) 0x006627d8: b9008a5f str wzr, [tr, #136] (exception) 0x006627dc: b9000fe0 str w0, [sp, #12] 0x006627e0: b9402be0 ldr w0, [sp, #40] 0x006627e4: f941a25e ldr lr, [tr, #832] (pUnlockObject) 0x006627e8: d63f03c0 blr lr suspend point dex PC: 0x000a GC map objects: v0 ([sp + #12]), v2 ([sp + #40])
解了锁之后,再把刚才暂存在sp+12的exception,调用pDeliverException抛出去。
0x006627ec: b9400fe0 ldr w0, [sp, #12] 0x006627f0: f942225e ldr lr, [tr, #1088] (pDeliverException) 0x006627f4: d63f03c0 blr lr suspend point dex PC: 0x000b GC map objects: v0 ([sp + #12]), v2 ([sp + #40])
最后还是pTestSuspend。
0x006627f8: f9421e5e ldr lr, [tr, #1080] (pTestSuspend) 0x006627fc: d63f03c0 blr lr suspend point dex PC: 0x0000 GC map objects: v2 ([sp + #40]) 0x00662800: 17ffffde b #-0x88 (addr 0x662778)
- ART世界探险(9) - 同步锁
- ART世界探险(7) - 数组
- ART世界探险(18) InlineMethod
- ART探险(1) - oatdump看到的世界
- ART世界探险(5) - 计算指令
- ART世界探险(6) - 流程控制指令
- ART世界探险(8) - 面向对象编程
- ART世界探险(10) - 异常处理
- ART世界探险(11) - OAT文件格式分析
- ART世界探险(13) - 初入dex2oat
- ART世界探险(2) - 从java byte code说起
- ART世界探险(14) - 快速编译器和优化编译器
- ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件
- ART世界探险(16) - 快速编译器下的方法编译
- ART世界探险(17) - 中层中间代码MIR
- ART世界探险(19) - 优化编译器的编译流程
- ART世界探险(20) - Android N上的编译流程
- ART世界探险(3) - ARM 64位CPU的架构快餐教程
- AMESim 14.0 win10环境下安装教程
- 使用IDEA Debug Spark源码
- 豆瓣图书搜索系统实验
- JAVA项目三:JAVA实现MD5文件校验
- 【c++】初步探索c++对象模型
- ART世界探险(9) - 同步锁
- Trie图入门题
- Java输出空心菱形,代码超简洁
- Http协议学习笔记
- 相机姿态估计(一)--PnP
- 迭代器
- Java中常见关键字
- python 学习(八)之 break 语句
- 大话设计模式27----解释器模式