Kotlin协程的字节码解析-1

    xiaoxiao2022-07-12  190

    1. 背景

    kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。

    本文是通过反编译协程相关class文件,分析协程的实现原理。

    2 Kotlin代码 T1.kt

    第一个基本协程代码

    import kotlinx.coroutines.* fun main() { println("Starting...") GlobalScope.launch { repeat(10) { i -> println("I'm sleeping $i ...") delay(500L) } } println("Hello,") Thread.sleep(20000L) }

    输出结果

    Starting... Hello, I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... I'm sleeping 3 ... I'm sleeping 4 ... I'm sleeping 5 ... I'm sleeping 6 ... I'm sleeping 7 ... I'm sleeping 8 ... I'm sleeping 9 ...

    3. 字节码

    T1.kt编译后出现两个class文件T1kt.class和T1kt$main$1.class

    3.1 T1kt.class

    public final class T1Kt { public static final void main(); Code: 0: ldc #11 // String Starting... 2: astore_0 3: iconst_0 4: istore_1 5: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 8: aload_0 9: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 12: getstatic #29 // Field kotlinx/coroutines/GlobalScope.INSTANCE:Lkotlinx/coroutines/GlobalScope; 15: checkcast #31 // class kotlinx/coroutines/CoroutineScope 18: aconst_null 19: aconst_null 20: new #33 // class T1Kt$main$1 23: dup 24: aconst_null 25: invokespecial #37 // Method T1Kt$main$1."<init>":(Lkotlin/coroutines/Continuation;)V 28: checkcast #39 // class kotlin/jvm/functions/Function2 31: iconst_3 32: aconst_null 33: invokestatic #45 // Method kotlinx/coroutines/BuildersKt.launch$default:(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/CoroutineContext;Lkotlinx/coroutines/CoroutineStart;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job; 36: pop 37: ldc #47 // String Hello, 39: astore_0 40: iconst_0 41: istore_1 42: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 45: aload_0 46: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 49: ldc2_w #48 // long 20000l 52: invokestatic #55 // Method java/lang/Thread.sleep:(J)V 55: return public static void main(java.lang.String[]); Code: 0: invokestatic #9 // Method main:()V 3: return }

    3.1.1 public static void main(java.lang.String[]) main

    自动创建了public static void main(java.lang.String[]) main方法,通过invokestatic字节码调用public static final void main方法。

    3.1.2 public static final void main

    此方法对应于T1.kt中的fun main方法

    0:从常量池加载字面量"Starting"9:打印"Starting"20:创建T1Kt$main$1类25:初始化T1Kt$main$1对象33:调用静态方法kotlinx/coroutines/BuildersKt.launch,入参是GlobalScope、null、null、T1Kt$main$1对象(Function2接口的实例)46:打印Hello52:休眠20000毫秒,等待协程执行完

    BuildersKt.launch方法的入参为CoroutineScope、CoroutineContext、CoroutineStart、Function2、Object。在此场景下:

    CoroutineScope:GlobalScope,默认的顶层协程CoroutineContext:四种协程上下文Unconfined、Default、newSingleThreadContext、runBlocking,此时使用Default调度器,使用一个JVM内的共享线程池CoroutineStart:DEFAULT,立即调度协程并使用指定的CoroutineContext

    3.2 T1kt$main$1.class

    T1kt$main$1继承了抽象类kotlin.coroutines.jvm.internal.SuspendLambda,并实现了接口kotlin.jvm.functions.Function2

    3.2.1 public final java.lang.Object invoke(java.lang.Object, java.lang.Object)

    协程执行入口

    0: aload_0 1: aload_1 2: aload_2 3: checkcast #149 // class kotlin/coroutines/Continuation 6: invokevirtual #151 // Method create:(Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; 9: checkcast #2 // class T1Kt$main$1 12: getstatic #109 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit; 15: invokevirtual #153 // Method invokeSuspend:(Ljava/lang/Object;)Ljava/lang/Object; 18: areturn 0-2:加载this和两个入参(CoroutineScope和Coroutine)15:调用实例方法invokeSuspend

    3.2.2 public final java.lang.Object invokeSuspend

    本方法为协程的具体执行逻辑。

    0: invokestatic #34 // Method kotlin/coroutines/intrinsics/IntrinsicsKt.getCOROUTINE_SUSPENDED:()Ljava/lang/Object; 3: astore 11 5: aload_0 6: getfield #37 // Field label:I 9: tableswitch { // 0 to 1 0: 32 1: 156 default: 190 } 32: aload_1 33: invokestatic #43 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V 36: aload_0 37: getfield #45 // Field p$:Lkotlinx/coroutines/CoroutineScope; 40: astore_2 41: bipush 10 43: istore_3 44: iconst_0 45: istore 4 47: iconst_0 48: istore 5 50: iconst_0 51: istore 5 53: iload_3 54: istore 6 56: iload 5 58: iload 6 60: if_icmpge 186 63: iload 5 65: invokestatic #51 // Method kotlin/coroutines/jvm/internal/Boxing.boxInt:(I)Ljava/lang/Integer; 68: checkcast #53 // class java/lang/Number 71: invokevirtual #57 // Method java/lang/Number.intValue:()I 74: istore 7 76: iconst_0 77: istore 8 79: new #59 // class java/lang/StringBuilder 82: dup 83: invokespecial #63 // Method java/lang/StringBuilder."<init>":()V 86: ldc #65 // String I'm sleeping 88: invokevirtual #69 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 91: iload 7 93: invokevirtual #72 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 96: ldc #74 // String ... 98: invokevirtual #69 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 101: invokevirtual #78 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 104: astore 9 106: iconst_0 107: istore 10 109: getstatic #84 // Field java/lang/System.out:Ljava/io/PrintStream; 112: aload 9 114: invokevirtual #89 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 117: ldc2_w #90 // long 500l 120: aload_0 121: aload_0 122: iload 5 124: putfield #93 // Field I$0:I 127: aload_0 128: iload 6 130: putfield #95 // Field I$1:I 133: aload_0 134: iload 7 136: putfield #97 // Field I$2:I 139: aload_0 140: iconst_1 141: putfield #37 // Field label:I 144: invokestatic #103 // Method kotlinx/coroutines/DelayKt.delay:(JLkotlin/coroutines/Continuation;)Ljava/lang/Object; 147: dup 148: aload 11 150: if_acmpne 179 153: aload 11 155: areturn 156: aload_0 157: getfield #97 // Field I$2:I 160: istore 7 162: aload_0 163: getfield #95 // Field I$1:I 166: istore 6 168: aload_0 169: getfield #93 // Field I$0:I 172: istore 5 174: aload_1 175: invokestatic #43 // Method kotlin/ResultKt.throwOnFailure:(Ljava/lang/Object;)V 178: aload_1 179: pop 180: iinc 5, 1 183: goto 56 186: getstatic #109 // Field kotlin/Unit.INSTANCE:Lkotlin/Unit; 189: areturn 190: new #111 // class java/lang/IllegalStateException 193: dup 194: ldc #113 // String call to 'resume' before 'invoke' with coroutine 196: invokespecial #116 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V 199: athrow 6: 获取对象37字段的值,并压入栈,37字段对应于T1Kt$main$1.label。 label是个int类型的标识,用于管理Coroutine的状态机9:由于分支集中,仅两个此处使用了tableswitch字节码,而非lookupswitch141:修改label为1,默认值0144:调用delay方法,挂起协程500毫秒150:回边判断179:goto回循环开始

    4. 总结

    Kotlin协程通过编译器织入字节码实现协程执行逻辑被放入一个内部匿名类,继承了抽象类kotlin.coroutines.jvm.internal.SuspendLambda,并实现了接口kotlin.jvm.functions.Function2调度器调度入口是Function2的invoke方法通过delay方法挂起协程,该方法不会阻塞线程
    最新回复(0)