JVM字节码
Posted rzbwyj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM字节码相关的知识,希望对你有一定的参考价值。
JVM字节码
1、通过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); } }
通过javap命令查看class文件中的字节码:
javap -v Test1.class > Test1.txt 其中, 可能的选项包括: ‐help ‐‐help ‐? 输出此用法消息 ‐version 版本信息 ‐v ‐verbose 输出附加信息 ‐l 输出行号和本地变量表 ‐public 仅显示公共类和成员 ‐protected 显示受保护的/公共类和成员 ‐package 显示程序包/受保护的/公共类和成员 (默认) ‐p ‐private 显示所有类和成员 ‐c 对代码进行反汇编 ‐s 输出内部类型签名 ‐sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列) ‐constants 显示最终常量 ‐classpath <path> 指定查找用户类文件的位置 ‐cp <path> 指定查找用户类文件的位置 ‐bootclasspath <path> 覆盖引导类文件的位置
输出的字节码为:
Classfile /root/study/workspace/Test1.class Last modified Aug 6, 2019; size 579 bytes MD5 checksum a96ef22100a65dc41cf1fda3e995d015 Compiled from "Test1.java" public class com.lailai.jvm.Test1 minor version: 0 major version: 52 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/lailai/jvm/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/lailai/jvm/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/lailai/jvm/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.lailai.jvm.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/lailai/jvm/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 6: 0 line 7: 2 line 8: 4 line 9: 8 line 10: 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"
输出内容大致分为4部分:
第一部分,显示了生成这个class的java源文件、版本信息、生成时间等。
第二部分,显示了该类中所涉及到常量池,共35个常量。
第三部分,显示了该类的构造器,编译器自动插入的。
第四部分,显示了main方法的信息。(需要重点关注)
2、常量池
官网文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140
3、描述符
3.1、字段描述符
官网文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2
英文的看不懂大佬我给你们中文的
3.2、方法描述符
官网: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
可以看到括号内为方法的参数,括号外面为方法的返回值。
I 表示 int类型
D 表示double类型
Ljava/langThread; 表示对Thread的引用
Ljava/lang/Object; 表示对Object的引用
4、解读方法字节码
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=2,操作栈大小为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 //将相减的结果压入到本地变量表中,位于下标为3的位置 //通过#2号在Constant pool中找到对应的常量,即可找到对应的引用 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 11: iload_3 //将本地变量表中下标为3的位置的元素压入操作栈(3) //通过#3号在Constant pool中找到对应的常量,即可找到对应的引用,进行方法调用 12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 15: return //返回 LineNumberTable: //行号的列表 line 6: 0 line 7: 2 line 8: 4 line 9: 8 line 10: 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"
5、图解
以上是关于JVM字节码的主要内容,如果未能解决你的问题,请参考以下文章