该插件主要功能为多线程优化,首先需要先看看这个插件所支持的功能有什么
- 降低线程数量
- 线程重命名
- 线程池优化
线程重命名,替换Thread
原JAVA文件
class Test {void te(){new Thread(new Runnable() {@Overridepublic void run() {}}).start();}}
优化后的JAVA文件
class Test {Test() {}void te() {ShadowThread.setThreadName(new ShadowThread(new Runnable() {public void run() {}}, "\u200bnet.mikaelzero.myapplication.Test"),"\u200bnet.mikaelzero.myapplication.Test").start();}}
优化前的字节码指令
Compiled from "Test.java"class net.mikaelzero.myapplication.Test {net.mikaelzero.myapplication.Test();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnvoid te();Code:0: new #2 // class java/lang/Thread3: dup4: new #3 // class net/mikaelzero/myapplication/Test$17: dup8: aload_09: invokespecial #4 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V12: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V15: invokevirtual #6 // Method java/lang/Thread.start:()V18: return}
优化后的字节码指令
Compiled from "Test.java"class net.mikaelzero.myapplication.Test {net.mikaelzero.myapplication.Test();Code:0: aload_01: invokespecial #11 // Method java/lang/Object."<init>":()V4: returnvoid te();Code:0: new #16 // class com/didiglobal/booster/instrument/ShadowThread3: dup4: new #7 // class net/mikaelzero/myapplication/Test$17: dup8: aload_09: invokespecial #19 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V12: ldc #21 // String net.mikaelzero.myapplication.Test14: invokespecial #24 // Method com/didiglobal/booster/instrument/ShadowThread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V17: ldc #21 // String net.mikaelzero.myapplication.Test19: invokestatic #28 // Method com/didiglobal/booster/instrument/ShadowThread.setThreadName:(Ljava/lang/Thread;Ljava/lang/String;)Ljava/lang/Thread;22: invokevirtual #33 // Method java/lang/Thread.start:()V25: return}
- 将Thread的全限定名替换为ShadowThread
- 将初始化Thread的指令(invokespecial)中的限定名替换为ShdowThread,且传入参数为String名称
- 调用ShadowThread.setThreadName,并传入字符串
private fun TypeInsnNode.transformNew(@Suppress("UNUSED_PARAMETER") context: TransformContext, klass: ClassNode, method: MethodNode, type: String, optimizable: Boolean = false) {this.find {(it.opcode == Opcodes.INVOKESPECIAL) &&(it is MethodInsnNode) &&(this.desc == it.owner && "<init>" == it.name)}?.isInstanceOf { init: MethodInsnNode ->// 1this.desc = type//2val rp = init.desc.lastIndexOf(')')init.apply {owner = typedesc = "${desc.substring(0, rp)}Ljava/lang/String;${if (optimizable) "Z" else ""}${desc.substring(rp)}"}//3method.instructions.insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))if (optimizable) {method.instructions.insertBefore(init, LdcInsnNode(optimizationEnabled))}} ?: throw TransformException("`invokespecial $desc` not found: ${klass.name}.${method.name}${method.desc}")}
- 通过重新设置desc将Thread替换为ShadowThread,比如 java/lang/Thread => com/didiglobal/booster/instrument/ShadowThread
- (10行到14行)替换构造参数的全限定名,比如net/mikaelzero/myapplication/Test$1.”
“:(Lnet/mikaelzero/myapplication/Test;)V 替换为 com/didiglobal/booster/instrument/ShadowThread.” “:(Ljava/lang/Runnable;Ljava/lang/String;)V - 因为ShadowThread多了一个String的参数,所以需要先插入一个字符串到构造函数的Instructions指令前面
从字节码可以看出,invokevirtual和invokespecial部分的desc都需要进行替换,比如
invokevirtual #33 // Method java/lang/Thread.start:()V
需要替换为
invokevirtual #33 // Method com/didiglobal/booster/instrument/ShadowThread.start:()V
如果原代码调用了setName,比如
thread.setName("hello")17: ldc #6 // String helo name19: invokevirtual #7 // Method java/lang/Thread.setName:(Ljava/lang/String;)V
需要替换为
thread.setName(ShadowThread.makeThreadName("helo", "\u200bnet.mikaelzero.myapplication.Test"));9 ldc #26 <helo name>21 ldc #21 <net.mikaelzero.myapplication.Test>23 invokestatic #30 <com/didiglobal/booster/instrument/ShadowThread.makeThreadName>26 invokevirtual #36 <java/lang/Thread.setName>
对应源码为
"setName(Ljava/lang/String;)V" -> {method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "makeThreadName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false))logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")this.owner = THREAD}
线程池优化
比如newFixedThreadPool 替换 为newOptimizedFixedThreadPool,主要还是和上面的处理类似,通过desc替换以及插入对应的String参数
"defaultThreadFactory" -> {val r = this.desc.lastIndexOf(')')val desc = "${this.desc.substring(0, r)}Ljava/lang/String;${this.desc.substring(r)}"logger.println(" * ${this.owner}.${this.name}${this.desc} => $SHADOW_EXECUTORS.${this.name}$desc: ${klass.name}.${method.name}${method.desc}")this.owner = SHADOW_EXECUTORSthis.desc = descmethod.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))}
降低线程数量
这些都是在配置线程池的时候做一个替换为自己的 ShadowExecutors,通过如下代码进行设置
public static ScheduledExecutorService newOptimizedSingleThreadScheduledExecutor(final String name) {final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(name));executor.setKeepAliveTime(DEFAULT_KEEP_ALIVE, TimeUnit.MILLISECONDS);executor.allowCoreThreadTimeOut(true);return executor;}public static ScheduledExecutorService newOptimizedSingleThreadScheduledExecutor(final ThreadFactory factory, final String name) {final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(factory, name));executor.setKeepAliveTime(DEFAULT_KEEP_ALIVE, TimeUnit.MILLISECONDS);executor.allowCoreThreadTimeOut(true);return executor;}
对于替换,和替换Thread的原理基本上是一致的。
