Kotlin挂起方法返回内联类时字节码含额外分支的原因咨询
问题:返回内联类的挂起方法invokeSuspend为何存在额外分支?
我们定义了如下@JvmInline修饰的内联类:
@JvmInline value class StringWrapper(val value: String)
并编写了DummyServiceWrapper类,包含多个挂起方法:
- printHello:仅调用其他挂起方法;
- printHello1:调用挂起方法后执行额外逻辑;
- printHello2:返回
String类型; - printHello3、printHello4:返回
StringWrapper内联类。
观察生成的Java字节码发现,返回内联类的挂起方法的invokeSuspend方法中存在额外分支逻辑(判断返回值是否为IntrinsicsKt.getCOROUTINE_SUSPENDED(),若非则执行StringWrapper.box-impl()),示例代码如下:
@Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; Object var10000 = DummyServiceWrapper.this.printHello3-og2vJxQ(this); return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : StringWrapper.box-impl((String)var10000); }
而返回非内联类型的挂起方法的invokeSuspend无此分支,示例代码如下:
@Nullable public final Object invokeSuspend(@NotNull Object $result) { this.result = $result; this.label |= Integer.MIN_VALUE; return DummyServiceWrapper.this.printHello1(this); }
问题:为何返回内联类的挂起方法的invokeSuspend中会存在该额外分支?
回答:
要搞清楚这个问题,得先明确两个核心规则:
- 内联类的JVM实现逻辑:
@JvmInline内联类是值类型的轻量包装,编译时会尽量直接用底层原始值(比如这里的String)来减少对象开销,但当需要把它作为Object类型传递时,必须通过box-impl()方法把原始值装箱成内联类实例。 - 协程挂起方法的返回约定:Kotlin协程的
invokeSuspend方法有两种返回情况:- 协程需要挂起时,返回
IntrinsicsKt.getCOROUTINE_SUSPENDED()这个特殊标记对象; - 协程执行完成时,返回方法的实际结果。
- 协程需要挂起时,返回
现在看这个分支的必要性:
- 对于返回非内联类型的挂起方法(比如
String),实际结果本身就是Object类型(String是引用类型),所以直接返回即可,不需要额外处理。 - 对于返回内联类的挂起方法,协程完成时的实际返回值是内联类的底层原始值(比如这里的
String),但invokeSuspend的方法签名要求返回Object。这时候必须做分支判断:- 如果返回的是
COROUTINE_SUSPENDED标记,直接返回它——不能对这个标记执行装箱,否则会把特殊标记错误包装成内联类实例; - 如果是实际的原始值,就通过
box-impl()把它装箱成StringWrapper实例,符合方法返回Object的要求。
- 如果返回的是
说白了,这个分支就是为了区分「协程挂起标记」和「实际返回值」,确保只有在协程真正完成时,才对原始值做内联类装箱,同时保证挂起标记能正确传递。
内容的提问来源于stack exchange,提问作者rahat
相关产品推荐
相关产品推荐

