该插件主要功能为多线程优化,首先需要先看看这个插件所支持的功能有什么
- 降低线程数量
- 线程重命名
- 线程池优化
线程重命名,替换Thread
原JAVA文件
class Test {
void te(){
new Thread(new Runnable() {
@Override
public 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_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
void te();
Code:
0: new #2 // class java/lang/Thread
3: dup
4: new #3 // class net/mikaelzero/myapplication/Test$1
7: dup
8: aload_0
9: invokespecial #4 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V
12: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
15: invokevirtual #6 // Method java/lang/Thread.start:()V
18: return
}
优化后的字节码指令
Compiled from "Test.java"
class net.mikaelzero.myapplication.Test {
net.mikaelzero.myapplication.Test();
Code:
0: aload_0
1: invokespecial #11 // Method java/lang/Object."<init>":()V
4: return
void te();
Code:
0: new #16 // class com/didiglobal/booster/instrument/ShadowThread
3: dup
4: new #7 // class net/mikaelzero/myapplication/Test$1
7: dup
8: aload_0
9: invokespecial #19 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V
12: ldc #21 // String net.mikaelzero.myapplication.Test
14: invokespecial #24 // Method com/didiglobal/booster/instrument/ShadowThread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
17: ldc #21 // String net.mikaelzero.myapplication.Test
19: 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:()V
25: 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 ->
// 1
this.desc = type
//2
val rp = init.desc.lastIndexOf(')')
init.apply {
owner = type
desc = "${desc.substring(0, rp)}Ljava/lang/String;${if (optimizable) "Z" else ""}${desc.substring(rp)}"
}
//3
method.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 name
19: 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_EXECUTORS
this.desc = desc
method.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的原理基本上是一致的。