• 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
        将本地变量中的数字做了相加,并且将数据压入到操作栈将操作栈中的数据,再次压入到本地变量中
      小结:
        可以通过查看字节码的方式对代码的底层做研究,探究其原理。
  • 相关阅读:
    修改Putty终端目录(ls命令)显示颜色
    在循环中进行提交的测试
    远程连接Redhat Linux配置
    如何部署Silverlight及Web Service
    SQL2008 的收缩日志
    WPF中DataGrid使用初步
    常用SQL
    DataGridView转datatable
    Ext程序规划入门
    下一代C#里的async和await
  • 原文地址:https://www.cnblogs.com/ws1149939228/p/12463267.html
Copyright © 2020-2023  润新知