zoukankan      html  css  js  c++  java
  • Tomcat8优化--JVM字节码

    JVM字节码

      前面我们通过tomcat本身的参数以及jvm的参数对tomcat做了优化,其实要想将应用程序跑的更快、效率更高,除了对tomcat容器以及jvm优化外,应用程序代码本身如果写的效率不高的,那么也是不行的,所以,对于程序本身的优化也就很重要了。
      对于程序本身的优化,可以借鉴很多前辈们的经验,但是有些时候,在从源码角度方面分析的话,不好鉴别出哪个效率高,如对字符串拼接的操作,是直接“+”号拼接效率高还是使用StringBuilder效率高?
      这个时候,就需要通过查看编译好的class文件中字节码,就可以找到答案。
      我们都知道,java编写应用,需要先通过javac命令编译成class文件,再通过jvm执行,jvm执行时是需要将class文件中的字节码载入到jvm进行运行的。

    通过javap命令查看class文件的字节码内容

    创建一个简单的测试类

    public class Test1 { 
        public static void main(String[] args) { 
            int a = 2; 
            int b = 5; 
            int c = b-a; 
            System.out.println(c); 
        } 
    }

    cmd打开窗口,使用命令

    javap ‐v Test01.class > Test01.txt
    

    查看Texst1.xml文件

    Classfile /E:/accp/Y2/进阶内容/JVM/jvmTest/JvmTest/JVM_Project1/target/classes/com/zn/Test1.class
      Last modified 2020-3-10; size 563 bytes
      MD5 checksum 227f9972c01499bef690d9371d0b0e14
      Compiled from "Test1.java"
    public class com.zn.Test1
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #5.#23         // java/lang/Object."<init>":()V
       #2 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = Methodref          #26.#27        // java/io/PrintStream.println:(I)V
       #4 = Class              #28            // com/zn/Test1
       #5 = Class              #29            // java/lang/Object
       #6 = Utf8               <init>
       #7 = Utf8               ()V
       #8 = Utf8               Code
       #9 = Utf8               LineNumberTable
      #10 = Utf8               LocalVariableTable
      #11 = Utf8               this
      #12 = Utf8               Lcom/zn/Test1;
      #13 = Utf8               main
      #14 = Utf8               ([Ljava/lang/String;)V
      #15 = Utf8               args
      #16 = Utf8               [Ljava/lang/String;
      #17 = Utf8               a
      #18 = Utf8               I
      #19 = Utf8               b
      #20 = Utf8               c
      #21 = Utf8               SourceFile
      #22 = Utf8               Test1.java
      #23 = NameAndType        #6:#7          // "<init>":()V
      #24 = Class              #30            // java/lang/System
      #25 = NameAndType        #31:#32        // out:Ljava/io/PrintStream;
      #26 = Class              #33            // java/io/PrintStream
      #27 = NameAndType        #34:#35        // println:(I)V
      #28 = Utf8               com/zn/Test1
      #29 = Utf8               java/lang/Object
      #30 = Utf8               java/lang/System
      #31 = Utf8               out
      #32 = Utf8               Ljava/io/PrintStream;
      #33 = Utf8               java/io/PrintStream
      #34 = Utf8               println
      #35 = Utf8               (I)V
    {
      public com.zn.Test1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/zn/Test1;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=4, args_size=1
             0: iconst_2
             1: istore_1
             2: iconst_5
             3: istore_2
             4: iload_2
             5: iload_1
             6: isub
             7: istore_3
             8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            11: iload_3
            12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
            15: return
          LineNumberTable:
            line 5: 0
            line 6: 2
            line 7: 4
            line 8: 8
            line 9: 15
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      16     0  args   [Ljava/lang/String;
                2      14     1     a   I
                4      12     2     b   I
                8       8     3     c   I
    }
    SourceFile: "Test1.java"
    

    内容大致分为四个部分:

      第一部分:显示了生成这个class的java源文件,版本信息,生成时间等;

      第二部分:显示了该类中涉及到常量池,共35个;

      第三部分:显示该类的构造器,编译器自动插入的;

      第四部分:显示了main方的信息;

    常量池

      在JDK6.0及之前版本。String Pool里放的都是字符串常量;

      在JDK1.7中,由于String #intern()发生了变化,因此String Pool中也可以存放于堆内的字符串对象的引用;

    官方文档

      https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140

    Constant  Type           value        说明

    CONSTANT_Class           7      类或接口的符号引用
    CONSTANT_Fieldref             9         字段的符号引用
    CONSTANT_Methodref        10      类中方法的符号引用
    CONSTANT_InterfaceMethodref    11        接口中方法的符号引用
    CONSTANT_String          8      字符串类型常量
    CONSTANT_Integer             3         整形常量
    CONSTANT_Float           4       浮点型常量
    CONSTANT_Long           5      长整型常量
    CONSTANT_Double          6      双精度浮点型常量
    CONSTANT_NameAndType       12     字段或方法的符号引用
    CONSTANT_Utf8               1         UTF-8编码的字符串
    CONSTANT_MethodHandle     15     表示方法句柄
    CONSTANT_MethodType      16     标志方法类型
    CONSTANT_InvokeDynamic      18        表示一个动态方法调用点

    描述符

    字段描述符

      官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2

       FieldTypeterm      Type      Interpretation     

        B           byte      signed byte        

        C           char      Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16

        D           double     double-precision floating-point value
        F           float      single-precision floating-point value
        I            int      integer
        J           long      long integer
        LClassName;     reference     an instance of class ClassName
        S           short      signed short
        Z           boolean     true or false
        [           reference    one array dimension

    方法描述符

      官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3

    实例

      The method descriptor for the method:

    Object m(int i, double d, Thread t) {...}
    

      is:

    (IDLjava/lang/Thread;)Ljava/lang/Object;
    

    解读方法字节码

    public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V    //方法描述,V表示该方法的返回值为void
        flags: ACC_PUBLIC, ACC_STATIC    //方法修饰符,public,static的个数
        Code:        //stack=2,操作栈的大小为2,locals=4,本地变量表大小,args_size=1,参数的个数
          stack=2, locals=4, args_size=1
             0: iconst_2    //将数字2值压入擦作栈,位于栈的最上面
             1: istore_1    //从操作栈中弹出一个元素(数字2),放入到本地变量表中,位于下标为1的位置(下标为0的是this)
             2: iconst_5    //将数据5值压入操作栈,位于栈的最上面
             3: istore_2    //操作栈中国弹出一个元素(5),放到本地变量表中,位于下标为2的位置
             4: iload_2    //将本地变量表中下标为2的位置元素压入操作栈(5)
             5: iload_1    //将本地变量表中下标为1的位置元素压入操作栈(2)
             6: isub      //操作栈中的2个数字相减
             7: istore_3    
             8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;  通过#2号找到对应的常量,即可找到对应的引用
            11: iload_3
            12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V  通过#3号找到对应的常量,即可找到对应的引用,进行方法调用
            15: return
          LineNumberTable:
            line 5: 0
            line 6: 2
            line 7: 4
            line 8: 8
            line 9: 15
          LocalVariableTable:    //本地变量表
            Start  Length  Slot  Name   Signature
                0      16     0  args   [Ljava/lang/String;
                2      14     1     a   I
                4      12     2     b   I
                8       8     3     c   I
    }
    SourceFile: "Test01.java"

    图解

     

     

    研究i++和++i的不同

      都知道,i++表示,先返回在+1;++i表示,先+1在返回;

        测试代码:

    public class Test02 {
        public static void main(String[] args) {
            new Test02().method01();
            new Test02().method02();
        }
        public void method01(){
            int i=1;
            int a=i++;
            System.out.println(a);
        }
        public void method02(){
            int i=1;
            int a=++i;
            System.out.println(a);
        }
    }

    查看class字节码

    javap -v Test02.class > Test02.txt
    
      Last modified 2020-3-9; size 792 bytes
      MD5 checksum cfa985a5953fb3c7a88de6d9fa95bdac
      Compiled from "Test02.java"
    public class com.wn.Test.Test02
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #8.#27         // java/lang/Object."<init>":()V
       #2 = Class              #28            // com/wn/Test/Test02
       #3 = Methodref          #2.#27         // com/wn/Test/Test02."<init>":()V
       #4 = Methodref          #2.#29         // com/wn/Test/Test02.method01:()V
       #5 = Methodref          #2.#30         // com/wn/Test/Test02.method02:()V
       #6 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;
       #7 = Methodref          #33.#34        // java/io/PrintStream.println:(I)V
       #8 = Class              #35            // java/lang/Object
       #9 = Utf8               <init>
      #10 = Utf8               ()V
      #11 = Utf8               Code
      #12 = Utf8               LineNumberTable
      #13 = Utf8               LocalVariableTable
      #14 = Utf8               this
      #15 = Utf8               Lcom/wn/Test/Test02;
      #16 = Utf8               main
      #17 = Utf8               ([Ljava/lang/String;)V
      #18 = Utf8               args
      #19 = Utf8               [Ljava/lang/String;
      #20 = Utf8               method01
      #21 = Utf8               i
      #22 = Utf8               I
      #23 = Utf8               a
      #24 = Utf8               method02
      #25 = Utf8               SourceFile
      #26 = Utf8               Test02.java
      #27 = NameAndType        #9:#10         // "<init>":()V
      #28 = Utf8               com/wn/Test/Test02
      #29 = NameAndType        #20:#10        // method01:()V
      #30 = NameAndType        #24:#10        // method02:()V
      #31 = Class              #36            // java/lang/System
      #32 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
      #33 = Class              #39            // java/io/PrintStream
      #34 = NameAndType        #40:#41        // println:(I)V
      #35 = Utf8               java/lang/Object
      #36 = Utf8               java/lang/System
      #37 = Utf8               out
      #38 = Utf8               Ljava/io/PrintStream;
      #39 = Utf8               java/io/PrintStream
      #40 = Utf8               println
      #41 = Utf8               (I)V
    {
      public com.wn.Test.Test02();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/wn/Test/Test02;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: new           #2                  // class com/wn/Test/Test02
             3: dup
             4: invokespecial #3                  // Method "<init>":()V
             7: invokevirtual #4                  // Method method01:()V
            10: new           #2                  // class com/wn/Test/Test02
            13: dup
            14: invokespecial #3                  // Method "<init>":()V
            17: invokevirtual #5                  // Method method02:()V
            20: return
          LineNumberTable:
            line 5: 0
            line 6: 10
            line 7: 20
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      21     0  args   [Ljava/lang/String;
    
      public void method01();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: iconst_1
             1: istore_1
             2: iload_1
             3: iinc          1, 1
             6: istore_2
             7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
            10: iload_2
            11: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
            14: return
          LineNumberTable:
            line 9: 0
            line 10: 2
            line 11: 7
            line 12: 14
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      15     0  this   Lcom/wn/Test/Test02;
                2      13     1     i   I
                7       8     2     a   I
    
      public void method02();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: iconst_1
             1: istore_1
             2: iinc          1, 1
             5: iload_1
             6: istore_2
             7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
            10: iload_2
            11: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
            14: return
          LineNumberTable:
            line 14: 0
            line 15: 2
            line 16: 7
            line 17: 14
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      15     0  this   Lcom/wn/Test/Test02;
                2      13     1     i   I
                7       8     2     a   I
    }
    SourceFile: "Test02.java"

    对比

    i++;

    public void method01();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: iconst_1    //将数字1压入到操作栈
             1: istore_1    //将数字1从操作栈弹出,压入到本地变量表中,下标为1
             2: iload_1    //将本地变量表中获取下标为1的数据,压入到操作栈中
             3: iinc          1, 1    //将本地变量中的1,在+1
             6: istore_2    //将数字1从操作栈弹出,压入到本地变量表中,下标为2
             7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
            10: iload_2    //从本地变量表中获取下标为2的数据,压入到操作栈中
            11: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
            14: return
          LineNumberTable:
            line 9: 0
            line 10: 2
            line 11: 7
            line 12: 14
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      15     0  this   Lcom/wn/Test/Test02;
                2      13     1     i   I
                7       8     2     a   I

    ++i;

    public void method02();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: iconst_1    //将数字1压入到操作栈中
             1: istore_1    //将数字1从操作栈弹出,压入到本地变量表中,下标为1
             2: iinc          1, 1    //将本地变量中的1,在+1
             5: iload_1    //从本地变量表中获取下标为1的数据,压入到操作栈中
             6: istore_2    //将数据2从操作栈弹出,压入到本地变量表中,下标为2
             7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
            10: iload_2    //将本地变量表中获取下标为2的数据,压入到操作栈中
            11: invokevirtual #7                  // Method java/io/PrintStream.println:(I)V
            14: return
          LineNumberTable:
            line 14: 0
            line 15: 2
            line 16: 7
            line 17: 14
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      15     0  this   Lcom/wn/Test/Test02;
                2      13     1     i   I
                7       8     2     a   I

    区别

      i++:

        只是在本地变量中对数字做了相加,并没有将数据压入操作栈;

        将前面拿到的数字1,再次从操作栈中拿到,压入到本地变量中;

      ++i:

        将本地变量中的数字做了相加,并且将数据压入到操作栈;

        将操作栈中的数据,再次压入到本地变量中;

      小结:可以通过查看字节码的方式对代码的底层做研究,探究其原理;

    字符串拼接

    常见的字符串拼接方式

    字符串的拼接在开发过程中使用是非常频繁的,常用的方式有三种:
    +号拼接: str+"456"
    StringBuilder拼接
    StringBuffer拼接
    StringBuffer是保证线程安全的,效率是比较低的,我们更多的是使用场景是不会涉及到
    线程安全的问题的,所以更多的时候会选择StringBuilder,效率会高一些。

    测试代码

    public class Test03 {
        public static void main(String[] args) {
            new Test03().m1();
            new Test03().m2();
        }
        public void m1(){
            String s1="123";
            String s2="456";
            String s3=s1+s2;
            System.out.println(s3);
        }
        public void m2(){
            String s1="123";
            String s2="456";
            StringBuilder sb=new StringBuilder();
            sb.append(s1);
            sb.append(s2);
            String s3=sb.toString();
            System.out.println(s3);
        }
    }

    查看class字节码

    javap -v Test03.class > Test03.txt
      Last modified 2020-3-9; size 1111 bytes
      MD5 checksum 2f52301c461e860c7985882234cbdd5d
      Compiled from "Test03.java"
    public class com.wn.Test.Test03
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #14.#36        // java/lang/Object."<init>":()V
       #2 = Class              #37            // com/wn/Test/Test03
       #3 = Methodref          #2.#36         // com/wn/Test/Test03."<init>":()V
       #4 = Methodref          #2.#38         // com/wn/Test/Test03.m1:()V
       #5 = Methodref          #2.#39         // com/wn/Test/Test03.m2:()V
       #6 = String             #40            // 123
       #7 = String             #41            // 456
       #8 = Class              #42            // java/lang/StringBuilder
       #9 = Methodref          #8.#36         // java/lang/StringBuilder."<init>":()V
      #10 = Methodref          #8.#43         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      #11 = Methodref          #8.#44         // java/lang/StringBuilder.toString:()Ljava/lang/String;
      #12 = Fieldref           #45.#46        // java/lang/System.out:Ljava/io/PrintStream;
      #13 = Methodref          #47.#48        // java/io/PrintStream.println:(Ljava/lang/String;)V
      #14 = Class              #49            // java/lang/Object
      #15 = Utf8               <init>
      #16 = Utf8               ()V
      #17 = Utf8               Code
      #18 = Utf8               LineNumberTable
      #19 = Utf8               LocalVariableTable
      #20 = Utf8               this
      #21 = Utf8               Lcom/wn/Test/Test03;
      #22 = Utf8               main
      #23 = Utf8               ([Ljava/lang/String;)V
      #24 = Utf8               args
      #25 = Utf8               [Ljava/lang/String;
      #26 = Utf8               m1
      #27 = Utf8               s1
      #28 = Utf8               Ljava/lang/String;
      #29 = Utf8               s2
      #30 = Utf8               s3
      #31 = Utf8               m2
      #32 = Utf8               sb
      #33 = Utf8               Ljava/lang/StringBuilder;
      #34 = Utf8               SourceFile
      #35 = Utf8               Test03.java
      #36 = NameAndType        #15:#16        // "<init>":()V
      #37 = Utf8               com/wn/Test/Test03
      #38 = NameAndType        #26:#16        // m1:()V
      #39 = NameAndType        #31:#16        // m2:()V
      #40 = Utf8               123
      #41 = Utf8               456
      #42 = Utf8               java/lang/StringBuilder
      #43 = NameAndType        #50:#51        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      #44 = NameAndType        #52:#53        // toString:()Ljava/lang/String;
      #45 = Class              #54            // java/lang/System
      #46 = NameAndType        #55:#56        // out:Ljava/io/PrintStream;
      #47 = Class              #57            // java/io/PrintStream
      #48 = NameAndType        #58:#59        // println:(Ljava/lang/String;)V
      #49 = Utf8               java/lang/Object
      #50 = Utf8               append
      #51 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
      #52 = Utf8               toString
      #53 = Utf8               ()Ljava/lang/String;
      #54 = Utf8               java/lang/System
      #55 = Utf8               out
      #56 = Utf8               Ljava/io/PrintStream;
      #57 = Utf8               java/io/PrintStream
      #58 = Utf8               println
      #59 = Utf8               (Ljava/lang/String;)V
    {
      public com.wn.Test.Test03();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/wn/Test/Test03;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: new           #2                  // class com/wn/Test/Test03
             3: dup
             4: invokespecial #3                  // Method "<init>":()V
             7: invokevirtual #4                  // Method m1:()V
            10: new           #2                  // class com/wn/Test/Test03
            13: dup
            14: invokespecial #3                  // Method "<init>":()V
            17: invokevirtual #5                  // Method m2:()V
            20: return
          LineNumberTable:
            line 5: 0
            line 6: 10
            line 7: 20
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      21     0  args   [Ljava/lang/String;
    
      public void m1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=4, args_size=1
             0: ldc           #6                  // String 123
             2: astore_1
             3: ldc           #7                  // String 456
             5: astore_2
             6: new           #8                  // class java/lang/StringBuilder
             9: dup
            10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
            13: aload_1
            14: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            17: aload_2
            18: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            21: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            24: astore_3
            25: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
            28: aload_3
            29: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            32: return
          LineNumberTable:
            line 9: 0
            line 10: 3
            line 11: 6
            line 12: 25
            line 13: 32
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      33     0  this   Lcom/wn/Test/Test03;
                3      30     1    s1   Ljava/lang/String;
                6      27     2    s2   Ljava/lang/String;
               25       8     3    s3   Ljava/lang/String;
    
      public void m2();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=5, args_size=1
             0: ldc           #6                  // String 123
             2: astore_1
             3: ldc           #7                  // String 456
             5: astore_2
             6: new           #8                  // class java/lang/StringBuilder
             9: dup
            10: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
            13: astore_3
            14: aload_3
            15: aload_1
            16: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            19: pop
            20: aload_3
            21: aload_2
            22: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            25: pop
            26: aload_3
            27: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            30: astore        4
            32: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
            35: aload         4
            37: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            40: return
          LineNumberTable:
            line 15: 0
            line 16: 3
            line 17: 6
            line 18: 14
            line 19: 20
            line 20: 26
            line 21: 32
            line 22: 40
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      41     0  this   Lcom/wn/Test/Test03;
                3      38     1    s1   Ljava/lang/String;
                6      35     2    s2   Ljava/lang/String;
               14      27     3    sb   Ljava/lang/StringBuilder;
               32       9     4    s3   Ljava/lang/String;
    }
    SourceFile: "Test03.java"

    从字节码中可以看出,m1()方法源码中是使用+好拼接,但是字节码中也被编译成了StringBuilder方式;

    所以可以得出结论,字符串拼接,+号和StringBuilder是相等的,效率一样;

    测试代码2

    public class Test04 {
        public static void main(String[] args) {
            new Test04().m1();
            new Test04().m2();
        }
        public void m1(){
            String str="";
            for (int i=0;i<5;i++){
                str=str+i;
            }
            System.out.println(str);
        }
        public void m2(){
            StringBuilder sb=new StringBuilder();
            for (int i=0;i<5;i++){
                sb.append(i);
            }
            System.out.println(sb.toString());
        }
    }

    m1()和m2哪个方法的效率高?

    查看class字节码2

     Last modified 2020-3-9; size 1189 bytes
      MD5 checksum 40930ac74643e9d0a1d5942e3767043e
      Compiled from "Test04.java"
    public class com.wn.Test.Test04
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #14.#39        // java/lang/Object."<init>":()V
       #2 = Class              #40            // com/wn/Test/Test04
       #3 = Methodref          #2.#39         // com/wn/Test/Test04."<init>":()V
       #4 = Methodref          #2.#41         // com/wn/Test/Test04.m1:()V
       #5 = Methodref          #2.#42         // com/wn/Test/Test04.m2:()V
       #6 = String             #43            //
       #7 = Class              #44            // java/lang/StringBuilder
       #8 = Methodref          #7.#39         // java/lang/StringBuilder."<init>":()V
       #9 = Methodref          #7.#45         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      #10 = Methodref          #7.#46         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      #11 = Methodref          #7.#47         // java/lang/StringBuilder.toString:()Ljava/lang/String;
      #12 = Fieldref           #48.#49        // java/lang/System.out:Ljava/io/PrintStream;
      #13 = Methodref          #50.#51        // java/io/PrintStream.println:(Ljava/lang/String;)V
      #14 = Class              #52            // java/lang/Object
      #15 = Utf8               <init>
      #16 = Utf8               ()V
      #17 = Utf8               Code
      #18 = Utf8               LineNumberTable
      #19 = Utf8               LocalVariableTable
      #20 = Utf8               this
      #21 = Utf8               Lcom/wn/Test/Test04;
      #22 = Utf8               main
      #23 = Utf8               ([Ljava/lang/String;)V
      #24 = Utf8               args
      #25 = Utf8               [Ljava/lang/String;
      #26 = Utf8               m1
      #27 = Utf8               i
      #28 = Utf8               I
      #29 = Utf8               str
      #30 = Utf8               Ljava/lang/String;
      #31 = Utf8               StackMapTable
      #32 = Class              #53            // java/lang/String
      #33 = Utf8               m2
      #34 = Utf8               sb
      #35 = Utf8               Ljava/lang/StringBuilder;
      #36 = Class              #44            // java/lang/StringBuilder
      #37 = Utf8               SourceFile
      #38 = Utf8               Test04.java
      #39 = NameAndType        #15:#16        // "<init>":()V
      #40 = Utf8               com/wn/Test/Test04
      #41 = NameAndType        #26:#16        // m1:()V
      #42 = NameAndType        #33:#16        // m2:()V
      #43 = Utf8
      #44 = Utf8               java/lang/StringBuilder
      #45 = NameAndType        #54:#55        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      #46 = NameAndType        #54:#56        // append:(I)Ljava/lang/StringBuilder;
      #47 = NameAndType        #57:#58        // toString:()Ljava/lang/String;
      #48 = Class              #59            // java/lang/System
      #49 = NameAndType        #60:#61        // out:Ljava/io/PrintStream;
      #50 = Class              #62            // java/io/PrintStream
      #51 = NameAndType        #63:#64        // println:(Ljava/lang/String;)V
      #52 = Utf8               java/lang/Object
      #53 = Utf8               java/lang/String
      #54 = Utf8               append
      #55 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
      #56 = Utf8               (I)Ljava/lang/StringBuilder;
      #57 = Utf8               toString
      #58 = Utf8               ()Ljava/lang/String;
      #59 = Utf8               java/lang/System
      #60 = Utf8               out
      #61 = Utf8               Ljava/io/PrintStream;
      #62 = Utf8               java/io/PrintStream
      #63 = Utf8               println
      #64 = Utf8               (Ljava/lang/String;)V
    {
      public com.wn.Test.Test04();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/wn/Test/Test04;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: new           #2                  // class com/wn/Test/Test04
             3: dup
             4: invokespecial #3                  // Method "<init>":()V
             7: invokevirtual #4                  // Method m1:()V
            10: new           #2                  // class com/wn/Test/Test04
            13: dup
            14: invokespecial #3                  // Method "<init>":()V
            17: invokevirtual #5                  // Method m2:()V
            20: return
          LineNumberTable:
            line 5: 0
            line 6: 10
            line 7: 20
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      21     0  args   [Ljava/lang/String;
    
      public void m1();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: ldc           #6                  // String
             2: astore_1
             3: iconst_0
             4: istore_2
             5: iload_2
             6: iconst_5
             7: if_icmpge     35
            10: new           #7                  // class java/lang/StringBuilder
            13: dup
            14: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
            17: aload_1
            18: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            21: iload_2
            22: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
            25: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            28: astore_1
            29: iinc          2, 1
            32: goto          5
            35: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
            38: aload_1
            39: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            42: return
          LineNumberTable:
            line 9: 0
            line 10: 3
            line 11: 10
            line 10: 29
            line 13: 35
            line 14: 42
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                5      30     2     i   I
                0      43     0  this   Lcom/wn/Test/Test04;
                3      40     1   str   Ljava/lang/String;
          StackMapTable: number_of_entries = 2
            frame_type = 253 /* append */
              offset_delta = 5
              locals = [ class java/lang/String, int ]
            frame_type = 250 /* chop */
              offset_delta = 29
    
      public void m2();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: new           #7                  // class java/lang/StringBuilder
             3: dup
             4: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
             7: astore_1
             8: iconst_0
             9: istore_2
            10: iload_2
            11: iconst_5
            12: if_icmpge     27
            15: aload_1
            16: iload_2
            17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
            20: pop
            21: iinc          2, 1
            24: goto          10
            27: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
            30: aload_1
            31: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            34: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            37: return
          LineNumberTable:
            line 16: 0
            line 17: 8
            line 18: 15
            line 17: 21
            line 20: 27
            line 21: 37
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               10      17     2     i   I
                0      38     0  this   Lcom/wn/Test/Test04;
                8      30     1    sb   Ljava/lang/StringBuilder;
          StackMapTable: number_of_entries = 2
            frame_type = 253 /* append */
              offset_delta = 10
              locals = [ class java/lang/StringBuilder, int ]
            frame_type = 250 /* chop */
              offset_delta = 16
    }
    SourceFile: "Test04.java"

      可以看出,m1()方法中的循环体内,每一次循环都会创建StringBuilder对象,效率低于m2()方法;

    小结

      使用字节码的方式可以很好的查看代码底层的执行,从而可以看出哪些实现效率高,哪些实现效率低。可以更好的对我们的代码做优化。让程序执行下列更高;

      

      

     
     
     
  • 相关阅读:
    python 包管理工具 pip 的配置
    Python 变量作用域 LEGB (下)—— Enclosing function locals
    Python 变量作用域 LEGB (上)—— Local,Global,Builtin
    2020 Java 面试题 小结 (答案慢慢补上,有错误请指出)
    mysql 根据日期(date)做年,月,日分组统计查询
    jvm指令
    正则表达式 分割地址 获取省市区详细地址
    .Net 异常记录
    WCF设计服务协议(一)
    plsql ORA-01789:查询块具有不正确的结果列数
  • 原文地址:https://www.cnblogs.com/dabrk/p/12464603.html
Copyright © 2011-2022 走看看