JVM字节码

简介:

  前面我们通过tomcat本身的参数以及jvm的参数对tomcat做了优化,其实要想将应用程序跑的更快、效率更高,除了对tomcat容器以及jvm优化外,应用程序代码本身如果写的效率不高的,那么也是不行的,所以,对于程序本身的优化也就很重要了。

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

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

  首先,看一个简单的ClassTest.class类的代码:

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

  接下来通过javap命令查看class文件中的字节码内容:

  首先切换到完整的路径

 

   使用javap -v ClassTest.class > ClassTest.txt命令将输出内容保存在文本中

  具体简介:

   第一个部分:

    显示生成class的java源文件的基本信息
     Classfile /D:/jquery/Y2/使用SSM框架开发企业级应用二/jvm_project/jvm_day01/target/classes/com/wdksoft/ClassTest.class
     Last modified 2020-3-11; size 585 bytes
     MD5 checksum 39fa2636495e5b4bf08da6decc537381
     Compiled from "ClassTest.java"
     public class com.wdksoft.ClassTest
     minor version: 0
     major version: 51
     flags: ACC_PUBLIC, ACC_SUPER

   第二个部分:

    显示该类所涉及到的常量池,共有35个常量

     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/wdksoft/ClassTest
     #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/wdksoft/ClassTest;
     #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 ClassTest.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/wdksoft/ClassTest
     #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.wdksoft.ClassTest();
     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/wdksoft/ClassTest;

   第四部分:

     显示main方法的信息

     public static void main(java.lang.String[]);
     descriptor: ([Ljava/lang/String;)V 方法的描述,V代表返回值为void
     flags: ACC_PUBLIC, ACC_STATIC 方法修饰符 public static
     Code:
     stack=2, locals=4, args_size=1 stack代表操作栈的大小 locals本地变量表大小 args_size代表参数个数
     0: iconst_2 将数据2压入到操作栈当中,位于栈顶
     1: istore_1 从操作栈当中弹出一个数据,放到本地变量表当中,下标为1,0是this,操作栈当中数据清空
     2: iconst_5 将数据5压入到操作栈当中,位于栈顶
     3: istore_2 从操作栈当中弹出一个数据,放到本地变量表当中,下标为2,0是this,操作栈当中数据清空
     4: iload_2 将本地变量表中的下标为2的数据压入到操作站
     5: iload_1 将本地变量表中的下标为1的数据压入到操作站
     6: isub 将操作栈中的两个数据相减
     7: istore_3 将相减的结果压入到本地本地变量表当中,下标为3
     8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
     11: iload_3 将本地变量表中下标为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

  内容大致分为4个部分:
     第一部分:显示了生成这个class的java源文件、版本信息、生成时间等。
     第二部分:显示了该类中所涉及到常量池,共35个常量。
     第三部分:显示该类的构造器,编译器自动插入的。
     第四部分:显示了main方的信息。(这个是需要我们重点关注的) 

2.研究i++与++i的不同

  直接运行查看字节码

   对比

  i++   

    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 

  ++i

    0: iconst_1 //将数字1压入到操作栈
    1: istore_1 //将数字1从操作栈弹出,压入到本地变量表中,下标为1
    2: iinc 1, 1// 将本地变量中的1,再+1
    5: iload_1 //从本地变量表中获取下标为1的数据(2),压入到操作栈中
    6: istore_2 //将数字2从操作栈弹出,压入到本地变量表中,下标为2
    7: getstatic #6 // Field
    java/lang/System.out:Ljava/io/PrintStream;
    10: iload_2 //从本地变量表中获取下标为2的数据(2),压入到操作栈中
    11: invokevirtual #7 // Method
    java/io/PrintStream.println:(I)V
    14: return

  由此可得:

  i++
    只是在本地变量中对数字做了相加,并没有将数据压入到操作栈将前面拿到的数字1,再次从操作栈中拿到,压入到本地变量中
  ++i
    将本地变量中的数字做了相加,并且将数据压入到操作栈将操作栈中的数据,再次压入到本地变量中
  小结:
    可以通过查看字节码的方式对代码的底层做研究,探究其原理。
posted @ 2020-03-11 16:33  琴昕LNS~  阅读(172)  评论(0编辑  收藏  举报