javac 只在 lambda、字符串拼接等地方生成 invokedynamic,但它是 JVM 提供给所有语言的通用调用点机制。这篇自己生成字节码,把 bootstrap 方法、CallSite 的链接时机和可变目标直接做出来(lambda 本身的实现见前面 Lambda 一篇)。
还没有调用任何方法时 bootstrap 被调用的次数:0 const1 调用 3 次之后 bootstrap 调用记录:[const1] const2 再调用 2 次之后 bootstrap 调用记录:[const1, const2](每个调用点只链接一次)
初始目标 v1:v1:x,bootstrap 记录 [mut1] MutableCallSite.setTarget(v2) 之后,同一个调用点再调用:v2:x,bootstrap 记录 [mut1](没有重新链接,只是换了目标)
第 1 次调用:BootstrapMethodError,cause = IllegalStateException: bootstrap 故意失败:bad 第 2 次调用:BootstrapMethodError,cause = 无 bootstrap 被调用的次数 = 1(失败的链接被缓存,同一个调用点不会重试)
const1 方法体:
0: aload_0
1: invokedynamic #15, 0 // InvokeDynamic #0:const1:(Ljava/lang/String;)Ljava/lang/String;
6: areturn
常量池里的 InvokeDynamic 项:
#15 = InvokeDynamic #0:#14 // #0:const1:(Ljava/lang/String;)Ljava/lang/String;
#18 = InvokeDynamic #0:#17 // #0:const2:(Ljava/lang/String;)Ljava/lang/String;
BootstrapMethods 属性:
0: #13 REF_invokeStatic Main.bsm:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments: