Kotlin协程的字节码解析-1

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:打印Hello
  • 52:休眠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字节码,而非lookupswitch
  • 141:修改label为1,默认值0
  • 144:调用delay方法,挂起协程500毫秒
  • 150:回边判断
  • 179:goto回循环开始

4. 总结

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

猜你喜欢

转载自blog.csdn.net/a860MHz/article/details/90483591