JVM-01

作者: 平头哥2 | 来源:发表于2019-06-19 21:24 被阅读0次

    编写HelloWorld.java

    public class HelloWorld{
        public static void main(String[] args){
            System.out.println("HelloWorld");
        }
    }
    

    编译:

    javac HelloWorld.java
    

    javap的使用:

    D:\>javap
    用法: javap <options> <classes>
    其中, 可能的选项包括:
      -help  --help  -?        输出此用法消息
      -version                 版本信息
      -v  -verbose             输出附加信息
      -l                       输出行号和本地变量表
      -public                  仅显示公共类和成员
      -protected               显示受保护的/公共类和成员
      -package                 显示程序包/受保护的/公共类
                               和成员 (默认)
      -p  -private             显示所有类和成员
      -c                       对代码进行反汇编
      -s                       输出内部类型签名
      -sysinfo                 显示正在处理的类的
                               系统信息 (路径, 大小, 日期, MD5 散列)
      -constants               显示最终常量
      -classpath <path>        指定查找用户类文件的位置
      -cp <path>               指定查找用户类文件的位置
      -bootclasspath <path>    覆盖引导类文件的位置
    

    其中-c -v -l -p -s是最常用的

    -c 选项

    $ javap -c HelloWorld.class
    Compiled from "HelloWorld.java"
    public class HelloWorld {
      public HelloWorld(); // ##这里默认会添加一个构造方法
        Code:
           0: aload_0   // 
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);//## main函数
        Code:
           0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc           #3                  // String HelloWorld
           5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
    }
    

    解释:

    1. aload_0 这个操作码是 aload_x 格式操作码中的一个。它们用来把**对象引用**加载到操作数栈。 x 表示正在被访问的局部变量数组的位置。在这里的 0 代表什么呢?我们知道非静态的函数都有第一个默认参数,那就是 this,这里的 aload_0 就是把 this 入栈
    2. invokespecial #1,invokespecial 指令调用实例初始化方法、私有方法、父类方法,#1 指的是常量池中的第一个,这里是方法引用java/lang/Object."<init>":()V,也即构造器函数
    3. return,这个操作码属于 ireturn、lreturn、freturn、dreturn、areturn 和 return 操作码组中的一员,其中 i 表示 int,返回整数,同类的还有 l 表示 long,f 表示 float,d 表示 double,a 表示 对象引用。没有前缀类型字母的 return 表示返回 void
    4. getstatic #2,getstatic 获取指定类的静态域,并将其值压入栈顶,#2 代表常量池中的第 2 个,这里表示的是java/lang/System.out:Ljava/io/PrintStream;,其实就是java.lang.System 类的静态变量 out(类型是 PrintStream)
    5. ldc #3、,ldc 用来将常量从运行时常量池压栈到操作数栈,#3 代表常量池的第三个(字符串 Hello, World)
    6. invokevirtual #4,invokevirutal 指令调用一个对象的实例方法,#4 表示 PrintStream.println(String) 函数引用,并把栈顶两个元素出栈

    -v选项

    $ javap -v HelloWorld.class
    Classfile /D:/ideaproject/jvm/HelloWorld.class
      Last modified 2019-6-19; size 411 bytes
      MD5 checksum 30c120f376b16361b854fd5c0d695ea6
      Compiled from "HelloWorld.java"
    public class HelloWorld
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
       #2 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = String             #18            // HelloWorld
       #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = Class              #18            // HelloWorld
       #6 = Class              #21            // java/lang/Object
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               main
      #12 = Utf8               ([Ljava/lang/String;)V
      #13 = Utf8               SourceFile
      #14 = Utf8               HelloWorld.java
      #15 = NameAndType        #7:#8          // "<init>":()V
      #16 = Class              #22            // java/lang/System
      #17 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
      #18 = Utf8               HelloWorld
      #19 = Class              #25            // java/io/PrintStream
      #20 = NameAndType        #26:#27        // println:(Ljava/lang/String;)V
      #21 = Utf8               java/lang/Object
      #22 = Utf8               java/lang/System
      #23 = Utf8               out
      #24 = Utf8               Ljava/io/PrintStream;
      #25 = Utf8               java/io/PrintStream
      #26 = Utf8               println
      #27 = Utf8               (Ljava/lang/String;)V
    {
      public HelloWorld();
        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 1: 0
    
      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: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String HelloWorld
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 3: 0
            line 4: 8
    }
    SourceFile: "HelloWorld.java"
    

    -l选项

    $ javap -l HelloWorld.class
    Compiled from "HelloWorld.java"
    public class HelloWorld {
      public HelloWorld();
        LineNumberTable:
          line 1: 0
    
      public static void main(java.lang.String[]);
        LineNumberTable:
          line 3: 0
          line 4: 8
    }
    

    -s选项:输出签名的类型描述符

    $ javap -s HelloWorld.class
    Compiled from "HelloWorld.java"
    public class HelloWorld {
      public HelloWorld();//构造方法
        descriptor: ()V //描述: ()表示没有参数,V表示返回值为void
    
      public static void main(java.lang.String[]); //main方法
        descriptor: ([Ljava/lang/String;)V // ([Ljava/lang/String;)表示参数为数组
    }
    

    修改java代码如下:

    public class HelloWorld{
    
        public int test01(byte b1, short s, int a, long l2, float f1, double d, char c, boolean bool,Integer it,String str){
            return 0;
        }
    }
    

    重新编译,查看方法签名

    $ javap -s HelloWorld.class
    Compiled from "HelloWorld.java"
    public class HelloWorld {
      public HelloWorld();
        descriptor: ()V
    
      public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
        descriptor: (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
    }
    
    

    查看调试信息:

    编译阶段加上-g参数:

    $ javac -g HelloWorld.java
    

    -l 选项

    $ javap -l HelloWorld.class
    Compiled from "HelloWorld.java"
    public class HelloWorld {
      public HelloWorld();
        LineNumberTable:
          line 1: 0
        LocalVariableTable:
          Start  Length  Slot  Name   Signature
              0       5     0  this   LHelloWorld;
    //this表明构造方法默认第一个参数是this,LHelloWorld,表示构造方法名字和类名一样
      public static void main(java.lang.String[]);
        LineNumberTable:
          line 4: 0
          line 5: 8
        LocalVariableTable:
          Start  Length  Slot  Name   Signature
              0       9     0  args   [Ljava/lang/String;
    //args:参数名字,[Ljava/lang/String:表示参数为String数组,数组以 '[L' 开头
      public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
        LineNumberTable:
          line 8: 0
        LocalVariableTable:
          Start  Length  Slot  Name   Signature
              0       2     0  this   LHelloWorld;//this表明成员方法默认第一个参数是this
              0       2     1    b1   B //第二个参数名字是b1, 类型为byte,B代表byte
              0       2     2     s   S //第三个参数名字是s, 类型为short,S代表short
              0       2     3     a   I //第四个参数名字是a, 类型为int,I代表int
              0       2     4    l2   J //第五个参数名字是l2, 类型为long,J代表long
              0       2     6    f1   F //第六个参数名字是f1, 类型为float,F代表float
              0       2     7     d   D //第七个参数名字是d, 类型为short,D代表double
              0       2     9     c   C //第八个参数名字是c, 类型为char,C代表char
              0       2    10  bool   Z //第九个参数名字是bool, 类型为boolean,Z代表boolean
              0       2    11    it   Ljava/lang/Integer; //第十个参数名字是it, 类型为Integer
                                       //引用类型以L开头
              0       2    12   str   Ljava/lang/String; //第十一个参数名字是str, 类型为String
    }
    

    -v 选项

    $ javap -v HelloWorld.class
    Classfile /D:/ideaproject/jvm/HelloWorld.class
      Last modified 2019-6-19; size 874 bytes
      MD5 checksum ee15f31a14c1582634907a7504bf4cf7
      Compiled from "HelloWorld.java"
    public class HelloWorld
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#42         // java/lang/Object."<init>":()V
       #2 = Fieldref           #43.#44        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = String             #45            // helloworld
       #4 = Methodref          #46.#47        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = Class              #48            // HelloWorld
       #6 = Class              #49            // java/lang/Object
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               LHelloWorld;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Utf8               args
      #17 = Utf8               [Ljava/lang/String;
      #18 = Utf8               test01
      #19 = Utf8               (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
      #20 = Utf8               b1
      #21 = Utf8               B
      #22 = Utf8               s
      #23 = Utf8               S
      #24 = Utf8               a
      #25 = Utf8               I
      #26 = Utf8               l2
      #27 = Utf8               J
      #28 = Utf8               f1
      #29 = Utf8               F
      #30 = Utf8               d
      #31 = Utf8               D
      #32 = Utf8               c
      #33 = Utf8               C
      #34 = Utf8               bool
      #35 = Utf8               Z
      #36 = Utf8               it
      #37 = Utf8               Ljava/lang/Integer;
      #38 = Utf8               str
      #39 = Utf8               Ljava/lang/String;
      #40 = Utf8               SourceFile
      #41 = Utf8               HelloWorld.java
      #42 = NameAndType        #7:#8          // "<init>":()V
      #43 = Class              #50            // java/lang/System
      #44 = NameAndType        #51:#52        // out:Ljava/io/PrintStream;
      #45 = Utf8               helloworld
      #46 = Class              #53            // java/io/PrintStream
      #47 = NameAndType        #54:#55        // println:(Ljava/lang/String;)V
      #48 = Utf8               HelloWorld
      #49 = Utf8               java/lang/Object
      #50 = Utf8               java/lang/System
      #51 = Utf8               out
      #52 = Utf8               Ljava/io/PrintStream;
      #53 = Utf8               java/io/PrintStream
      #54 = Utf8               println
      #55 = Utf8               (Ljava/lang/String;)V
    {
      public HelloWorld();
        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 1: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   LHelloWorld;
    
      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: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #3                  // String helloworld
             5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 4: 0
            line 5: 8
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       9     0  args   [Ljava/lang/String;
    
      public int test01(byte, short, int, long, float, double, char, boolean, java.lang.Integer, java.lang.String);
        descriptor: (BSIJFDCZLjava/lang/Integer;Ljava/lang/String;)I
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=13, args_size=11 //参数个数为11
             0: iconst_0
             1: ireturn
          LineNumberTable:
            line 8: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       2     0  this   LHelloWorld;
                0       2     1    b1   B
                0       2     2     s   S
                0       2     3     a   I
                0       2     4    l2   J
                0       2     6    f1   F
                0       2     7     d   D
                0       2     9     c   C
                0       2    10  bool   Z
                0       2    11    it   Ljava/lang/Integer;
                0       2    12   str   Ljava/lang/String;
    }
    SourceFile: "HelloWorld.java"
    

    以16进制查看HelloWorld.class文件:

    # 将class文件转换成16进制
    $ xxd HelloWorld.class HelloWorld.txt
    $ ls
    HelloWorld.class  HelloWorld.java  HelloWorld.txt
    $ cat HelloWorld.txt
    00000000: cafe babe 0000 0034 001c 0a00 0600 0f09  .......4........
    00000010: 0010 0011 0800 120a 0013 0014 0700 1207  ................
    00000020: 0015 0100 063c 696e 6974 3e01 0003 2829  .....<init>...()
    00000030: 5601 0004 436f 6465 0100 0f4c 696e 654e  V...Code...LineN
    00000040: 756d 6265 7254 6162 6c65 0100 046d 6169  umberTable...mai
    00000050: 6e01 0016 285b 4c6a 6176 612f 6c61 6e67  n...([Ljava/lang
    00000060: 2f53 7472 696e 673b 2956 0100 0a53 6f75  /String;)V...Sou
    00000070: 7263 6546 696c 6501 000f 4865 6c6c 6f57  rceFile...HelloW
    00000080: 6f72 6c64 2e6a 6176 610c 0007 0008 0700  orld.java.......
    00000090: 160c 0017 0018 0100 0a48 656c 6c6f 576f  .........HelloWo
    000000a0: 726c 6407 0019 0c00 1a00 1b01 0010 6a61  rld...........ja
    000000b0: 7661 2f6c 616e 672f 4f62 6a65 6374 0100  va/lang/Object..
    000000c0: 106a 6176 612f 6c61 6e67 2f53 7973 7465  .java/lang/Syste
    000000d0: 6d01 0003 6f75 7401 0015 4c6a 6176 612f  m...out...Ljava/
    000000e0: 696f 2f50 7269 6e74 5374 7265 616d 3b01  io/PrintStream;.
    000000f0: 0013 6a61 7661 2f69 6f2f 5072 696e 7453  ..java/io/PrintS
    00000100: 7472 6561 6d01 0007 7072 696e 746c 6e01  tream...println.
    00000110: 0015 284c 6a61 7661 2f6c 616e 672f 5374  ..(Ljava/lang/St
    00000120: 7269 6e67 3b29 5600 2100 0500 0600 0000  ring;)V.!.......
    00000130: 0000 0200 0100 0700 0800 0100 0900 0000  ................
    00000140: 1d00 0100 0100 0000 052a b700 01b1 0000  .........*......
    00000150: 0001 000a 0000 0006 0001 0000 0001 0009  ................
    00000160: 000b 000c 0001 0009 0000 0025 0002 0001  ...........%....
    00000170: 0000 0009 b200 0212 03b6 0004 b100 0000  ................
    00000180: 0100 0a00 0000 0a00 0200 0000 0300 0800  ................
    00000190: 0400 0100 0d00 0000 0200 0e              ...........
    

    class文件各个部分的解释

    1. 魔数

      cafe babe

      class 的魔数为 0xCAFEBABE, 这个魔数是 JVM 识别 .class 文件的标志,虚拟机在加载类文件之前会先检查这四个字节,如果不是 0xCAFEBABE 则拒绝加载该文件

    相关文章

      网友评论

          本文标题:JVM-01

          本文链接:https://www.haomeiwen.com/subject/wvvlqctx.html