该插件主要功能为多线程优化,首先需要先看看这个插件所支持的功能有什么

  • 降低线程数量
  • 线程重命名
  • 线程池优化

线程重命名,替换Thread

原JAVA文件

  1. class Test {
  2. void te(){
  3. new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. }
  7. }).start();
  8. }
  9. }

优化后的JAVA文件

  1. class Test {
  2. Test() {
  3. }
  4. void te() {
  5. ShadowThread.setThreadName(
  6. new ShadowThread(new Runnable() {
  7. public void run() {
  8. }
  9. }, "\u200bnet.mikaelzero.myapplication.Test"),
  10. "\u200bnet.mikaelzero.myapplication.Test")
  11. .start();
  12. }
  13. }

优化前的字节码指令

  1. Compiled from "Test.java"
  2. class net.mikaelzero.myapplication.Test {
  3. net.mikaelzero.myapplication.Test();
  4. Code:
  5. 0: aload_0
  6. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  7. 4: return
  8. void te();
  9. Code:
  10. 0: new #2 // class java/lang/Thread
  11. 3: dup
  12. 4: new #3 // class net/mikaelzero/myapplication/Test$1
  13. 7: dup
  14. 8: aload_0
  15. 9: invokespecial #4 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V
  16. 12: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
  17. 15: invokevirtual #6 // Method java/lang/Thread.start:()V
  18. 18: return
  19. }

优化后的字节码指令

  1. Compiled from "Test.java"
  2. class net.mikaelzero.myapplication.Test {
  3. net.mikaelzero.myapplication.Test();
  4. Code:
  5. 0: aload_0
  6. 1: invokespecial #11 // Method java/lang/Object."<init>":()V
  7. 4: return
  8. void te();
  9. Code:
  10. 0: new #16 // class com/didiglobal/booster/instrument/ShadowThread
  11. 3: dup
  12. 4: new #7 // class net/mikaelzero/myapplication/Test$1
  13. 7: dup
  14. 8: aload_0
  15. 9: invokespecial #19 // Method net/mikaelzero/myapplication/Test$1."<init>":(Lnet/mikaelzero/myapplication/Test;)V
  16. 12: ldc #21 // String net.mikaelzero.myapplication.Test
  17. 14: invokespecial #24 // Method com/didiglobal/booster/instrument/ShadowThread."<init>":(Ljava/lang/Runnable;Ljava/lang/String;)V
  18. 17: ldc #21 // String net.mikaelzero.myapplication.Test
  19. 19: invokestatic #28 // Method com/didiglobal/booster/instrument/ShadowThread.setThreadName:(Ljava/lang/Thread;Ljava/lang/String;)Ljava/lang/Thread;
  20. 22: invokevirtual #33 // Method java/lang/Thread.start:()V
  21. 25: return
  22. }
  1. 将Thread的全限定名替换为ShadowThread
  2. 将初始化Thread的指令(invokespecial)中的限定名替换为ShdowThread,且传入参数为String名称
  3. 调用ShadowThread.setThreadName,并传入字符串
  1. private fun TypeInsnNode.transformNew(@Suppress("UNUSED_PARAMETER") context: TransformContext, klass: ClassNode, method: MethodNode, type: String, optimizable: Boolean = false) {
  2. this.find {
  3. (it.opcode == Opcodes.INVOKESPECIAL) &&
  4. (it is MethodInsnNode) &&
  5. (this.desc == it.owner && "<init>" == it.name)
  6. }?.isInstanceOf { init: MethodInsnNode ->
  7. // 1
  8. this.desc = type
  9. //2
  10. val rp = init.desc.lastIndexOf(')')
  11. init.apply {
  12. owner = type
  13. desc = "${desc.substring(0, rp)}Ljava/lang/String;${if (optimizable) "Z" else ""}${desc.substring(rp)}"
  14. }
  15. //3
  16. method.instructions.insertBefore(init, LdcInsnNode(makeThreadName(klass.className)))
  17. if (optimizable) {
  18. method.instructions.insertBefore(init, LdcInsnNode(optimizationEnabled))
  19. }
  20. } ?: throw TransformException("`invokespecial $desc` not found: ${klass.name}.${method.name}${method.desc}")
  21. }
  1. 通过重新设置desc将Thread替换为ShadowThread,比如 java/lang/Thread => com/didiglobal/booster/instrument/ShadowThread
  2. (10行到14行)替换构造参数的全限定名,比如net/mikaelzero/myapplication/Test$1.”“:(Lnet/mikaelzero/myapplication/Test;)V 替换为 com/didiglobal/booster/instrument/ShadowThread.”“:(Ljava/lang/Runnable;Ljava/lang/String;)V
  3. 因为ShadowThread多了一个String的参数,所以需要先插入一个字符串到构造函数的Instructions指令前面

从字节码可以看出,invokevirtual和invokespecial部分的desc都需要进行替换,比如

  1. invokevirtual #33 // Method java/lang/Thread.start:()V

需要替换为

  1. invokevirtual #33 // Method com/didiglobal/booster/instrument/ShadowThread.start:()V

如果原代码调用了setName,比如

  1. thread.setName("hello")
  2. 17: ldc #6 // String helo name
  3. 19: invokevirtual #7 // Method java/lang/Thread.setName:(Ljava/lang/String;)V

需要替换为

  1. thread.setName(ShadowThread.makeThreadName("helo", "\u200bnet.mikaelzero.myapplication.Test"));
  2. 9 ldc #26 <helo name>
  3. 21 ldc #21 <net.mikaelzero.myapplication.Test>
  4. 23 invokestatic #30 <com/didiglobal/booster/instrument/ShadowThread.makeThreadName>
  5. 26 invokevirtual #36 <java/lang/Thread.setName>

对应源码为

  1. "setName(Ljava/lang/String;)V" -> {
  2. method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
  3. method.instructions.insertBefore(this, MethodInsnNode(Opcodes.INVOKESTATIC, SHADOW_THREAD, "makeThreadName", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false))
  4. logger.println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
  5. this.owner = THREAD
  6. }

线程池优化

比如newFixedThreadPool 替换 为newOptimizedFixedThreadPool,主要还是和上面的处理类似,通过desc替换以及插入对应的String参数

  1. "defaultThreadFactory" -> {
  2. val r = this.desc.lastIndexOf(')')
  3. val desc = "${this.desc.substring(0, r)}Ljava/lang/String;${this.desc.substring(r)}"
  4. logger.println(" * ${this.owner}.${this.name}${this.desc} => $SHADOW_EXECUTORS.${this.name}$desc: ${klass.name}.${method.name}${method.desc}")
  5. this.owner = SHADOW_EXECUTORS
  6. this.desc = desc
  7. method.instructions.insertBefore(this, LdcInsnNode(makeThreadName(klass.className)))
  8. }

降低线程数量

这些都是在配置线程池的时候做一个替换为自己的 ShadowExecutors,通过如下代码进行设置

  1. public static ScheduledExecutorService newOptimizedSingleThreadScheduledExecutor(final String name) {
  2. final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(name));
  3. executor.setKeepAliveTime(DEFAULT_KEEP_ALIVE, TimeUnit.MILLISECONDS);
  4. executor.allowCoreThreadTimeOut(true);
  5. return executor;
  6. }
  7. public static ScheduledExecutorService newOptimizedSingleThreadScheduledExecutor(final ThreadFactory factory, final String name) {
  8. final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(factory, name));
  9. executor.setKeepAliveTime(DEFAULT_KEEP_ALIVE, TimeUnit.MILLISECONDS);
  10. executor.allowCoreThreadTimeOut(true);
  11. return executor;
  12. }

对于替换,和替换Thread的原理基本上是一致的。