本文只是简单入门demo演示,大家想要深入了解还是看官网文档或相关书籍比较好.
英文好的可以去https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
当然,如果英文不好的,可以看https://blog.csdn.net/weelyy/article/details/78969412
当然,大家如果有好的其它资源,欢迎推荐
java源码
package cn.jvm;
public class Test1 {
public static void main(String[] args) {
int a=5;
int b=9;
int c=11;
int d=a+b-c;
System.out.println(d);
}
}
class文件:
一般文本编辑器打开
说实话,能看懂这堆乱码的,那都是鬼才.
漱壕 4 % cn/jvm/Test1 java/lang/Object <init> ()V Code
LineNumberTable LocalVariableTable this Lcn/jvm/Test1; main ([Ljava/lang/String;)V java/lang/System out Ljava/io/PrintStream;
java/io/PrintStream println (I)V args [Ljava/lang/String; a I b c d
SourceFile
Test1.java ! / *??
~ < =>`d6???
4 ! " # $
16进制编辑器打开(在编辑器中是以表格的形式展示,拷贝出来就没有表格了)
16进制编辑器打开后,可以看了.不过,能看懂的人,脑子都不是一般人能比的.所以,作为普通人,我们还是找个更简单的方式.
CAFEBABE00000034002507000201000C636E2F6A766D2F54657374310700040100106A6176612F6C616E672F4F626A6563740100063C696E69743E010003282956010004436F64650A000300090C0005000601000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C650100047468697301000E4C636E2F6A766D2F54657374313B0100046D61696E010016285B4C6A6176612F6C616E672F537472696E673B295609001100130700120100106A6176612F6C616E672F53797374656D0C001400150100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0A001700190700180100136A6176612F696F2F5072696E7453747265616D0C001A001B0100077072696E746C6E01000428492956010004617267730100135B4C6A6176612F6C616E672F537472696E673B010001610100014901000162010001630100016401000A536F7572636546696C6501000A54657374312E6A617661002100010003000000000002000100050006000100070000002F00010001000000052AB70008B100000002000A00000006000100000003000B0000000C000100000005000C000D00000009000E000F000100070000007E0002000500000018083C10093D100B3E1B1C601D643604B200101504B60016B100000002000A0000001A00060000000700020008000500090008000A000F000B0017000C000B00000034000500000018001C001D000000020016001E001F0001000500130020001F0002000800100021001F0003000F00090022001F
使用javap命令,转成比较容易看懂的
javap -v Test1.class > Test1.txt
#当然,如果只看方法部分,可以使用 -c
//class字节码内容
Classfile /E:/code/test02/bin/cn/jvm/Test1.class
Last modified 2020-5-13; size 589 bytes
MD5 checksum e7b5f7b08dc9a21fcd20374c4639474f
Compiled from "Test1.java"
public class cn.jvm.Test1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // cn/jvm/Test1
#2 = Utf8 cn/jvm/Test1
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/jvm/Test1;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Fieldref #17.#19 // java/lang/System.out:Ljava/io/PrintStream;
#17 = Class #18 // java/lang/System
#18 = Utf8 java/lang/System
#19 = NameAndType #20:#21 // out:Ljava/io/PrintStream;
#20 = Utf8 out
#21 = Utf8 Ljava/io/PrintStream;
#22 = Methodref #23.#25 // java/io/PrintStream.println:(I)V
#23 = Class #24 // java/io/PrintStream
#24 = Utf8 java/io/PrintStream
#25 = NameAndType #26:#27 // println:(I)V
#26 = Utf8 println
#27 = Utf8 (I)V
#28 = Utf8 args
#29 = Utf8 [Ljava/lang/String;
#30 = Utf8 a
#31 = Utf8 I
#32 = Utf8 b
#33 = Utf8 c
#34 = Utf8 d
#35 = Utf8 SourceFile
#36 = Utf8 Test1.java
{
public cn.jvm.Test1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/jvm/Test1;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: iconst_5
1: istore_1
2: bipush 9
4: istore_2
5: bipush 11
7: istore_3
8: iload_1
9: iload_2
10: iadd
11: iload_3
12: isub
13: istore 4
15: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload 4
20: invokevirtual #22 // Method java/io/PrintStream.println:(I)V
23: return
LineNumberTable:
line 6: 0
line 7: 2
line 8: 5
line 9: 8
line 10: 15
line 11: 23
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 args [Ljava/lang/String;
2 22 1 a I
5 19 2 b I
8 16 3 c I
15 9 4 d I
}
SourceFile: "Test1.java"
//这一部分,显示的是class文件的一些相关信息,比如路径,修改时间,大小,编译的java文件,版本号,权限等
Classfile /E:/code/test02/bin/cn/jvm/Test1.class
Last modified 2020-5-13; size 589 bytes
MD5 checksum e7b5f7b08dc9a21fcd20374c4639474f
Compiled from "Test1.java"
public class cn.jvm.Test1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
权限标识符
名称 | 值 | 说明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public 可以从其程序包外部进行访问。 |
ACC_FINAL | 0x0010 | final 不能被继承 |
ACC_SUPER | 0x0020 | 用于兼容早期编译器,新编译器都有该标志 |
ACC_INTERFACE | 0x0200 | 接口 而不是一个类。 |
ACC_ABSTRACT | 0x0400 | 抽象类 不能实例化。 |
ACC_SYNTHETIC | 0x1000 | 由编译器产生,在源代码中不存在。 |
ACC_ANNOTATION | 0x2000 | 注释类型, |
ACC_ENUM | 0x4000 | 枚举类型 |
//常量池部分,共有36个.常量池的索引从1开始
Constant pool:
#1 = Class #2 //Class代表类或接口的引用,什么类呢,看 #2,原来是cn/jvm/Test1
#2 = Utf8 cn/jvm/Test1 //编码为Utf8的字符串cn/jvm/Test1
#3 = Class #4 //Class代表类或接口的引用 #4 java/lang/Object
#4 = Utf8 java/lang/Object //编码为Utf8的字符串java/lang/Object
#5 = Utf8 <init> //编码为Utf8的字符串<init>
#6 = Utf8 ()V //编码为Utf8的字符串()V ,无参,返回void
#7 = Utf8 Code //编码为Utf8的字符串Code
#8 = Methodref #3.#9 //方法的引用, #3.#9,#3调用#9 也就是java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable //编码为Utf8的字符串LineNumberTable
#11 = Utf8 LocalVariableTable //编码为Utf8的字符串LocalVariableTable
#12 = Utf8 this //编码为Utf8的字符串this
#13 = Utf8 Lcn/jvm/Test1; //编码为Utf8的字符串Lcn/jvm/Test1;
#14 = Utf8 main //编码为Utf8的字符串main
#15 = Utf8 ([Ljava/lang/String;)V //编码为Utf8的字符串 ([Ljava/lang/String;)V 参数是String数组,返回值是void
#16 = Fieldref #17.#19 // java/lang/System.out:Ljava/io/PrintStream;
#17 = Class #18 // java/lang/System
#18 = Utf8 java/lang/System //编码为Utf8的字符串 java/lang/System
#19 = NameAndType #20:#21 // out:Ljava/io/PrintStream;
#20 = Utf8 out //编码为Utf8的字符串 out
#21 = Utf8 Ljava/io/PrintStream; //编码为Utf8的字符串 Ljava/io/PrintStream; PrintStream类
#22 = Methodref #23.#25 // java/io/PrintStream.println:(I)V
#23 = Class #24 // java/io/PrintStream
#24 = Utf8 java/io/PrintStream //编码为Utf8的字符串 java/io/PrintStream
#25 = NameAndType #26:#27 // println:(I)V
#26 = Utf8 println //编码为Utf8的字符串 println
#27 = Utf8 (I)V //编码为Utf8的字符串 (I)V 参数是int 类型,返回是void
#28 = Utf8 args //编码为Utf8的字符串 args
#29 = Utf8 [Ljava/lang/String; //编码为Utf8的字符串 [Ljava/lang/String; String数组
#31 = Utf8 I //编码为Utf8的字符串 I
#32 = Utf8 b //编码为Utf8的字符串 b
#33 = Utf8 c //编码为Utf8的字符串 c
#34 = Utf8 d //编码为Utf8的字符串 d
#35 = Utf8 SourceFile //编码为Utf8的字符串SourceFile
#36 = Utf8 Test1.java //编码为Utf8的字符串 Test1.java
常量类型
Constant Type(常量类型) | Value | 说明 |
---|---|---|
CONSTANT_Utf8 | 1 | UTF-8编码的字符串 |
CONSTANT_Integer | 3 | 整形常量 |
CONSTANT_Float | 4 | 浮点型常量 |
CONSTANT_Long | 5 | 长整型常量 |
CONSTANT_Double | 6 | 双精度浮点型常量 |
CONSTANT_Class | 7 | 类或接口的符号引用 |
CONSTANT_String | 8 | 字符串类型常量 |
CONSTANT_Fieldref | 9 | 字段的符号引用 |
CONSTANT_Methodref | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType | 12 | 字段或方法的符号引用 |
CONSTANT_MethodHandle | 15 | 表示方法句柄 |
CONSTANT_MethodType | 16 | 标志方法类型 |
CONSTANT_InvokeDynamic | 18 | 表示一个动态方法调用点 |
字段描述符
FieldType term | 类型 | 说明 |
---|---|---|
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 |
L ClassName ; | reference | an instance of class ClassName |
S | short | signed short |
Z | boolean | true or false |
[ | reference | one array dimension |
//方法信息
//构造方法
public cn.jvm.Test1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/jvm/Test1;
//main方法
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
//操作栈大小为2,本地变量有5个,参数个数是1
0: iconst_5 //将数字5压到操作栈栈顶
1: istore_1 //从操作栈中弹出一个数(5),放入到下表为1的本地变量中,非静态函数本地变量中下表为0的是this
2: bipush 9 //将一个byte数量9压倒栈顶
4: istore_2 //将栈里的数(9)弹出放到本地变量表中,下表为2的位置
5: bipush 11 //将一个byte数量11压倒栈顶
7: istore_3 //将栈里的数(11)弹出放到本地变量表中,下表为2的位置
8: iload_1 //将本地变量表中下表为1的数压入栈中 5
9: iload_2 //将本地变量表中下表为2的数压入栈中 9
10: iadd //栈中两个数相加 14
11: iload_3 //将本地变量表中下表为3的数压入栈中 11
12: isub //栈中数字相减
13: istore 4 //将栈中数字弹出,放到本地变量表中的4
15: getstatic #16 //获取指定类的静态域,并将其值压入栈顶 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload 4 //将本地变量表中下表为4的数压入栈中
20: invokevirtual #22 //调用实例方法 // Method java/io/PrintStream.println:(I)V
23: return //返回
jvm指令
名称 | 说明 |
---|---|
iconst_5 | 将int类型常量5压到操作栈栈顶 |
istore_1 | 从操作栈中弹出一个数,放入到下表为1的本地变量 |
bipush 9 | 将byte类型常量9压到操作栈栈顶 |
iload_1 | 将本地变量表中下表为1的数压入栈中 |
iadd | 栈顶两int数值相加,并且结果进栈 |
isub | 栈顶两int型数值相减,并且结果进栈 |
getstatic | 获取指定类的静态域,并将其值压入栈顶 |
invokevirtual | 调用实例方法 |
return | 当前方法返回void |
更详细的内容,可以去看官网的JVM指令文档,可以是https://www.cnblogs.com/lsy131479/p/11201241.html或者https://blog.csdn.net/zhangpan19910604/article/details/52254053这些文章
//行数对比,
LineNumberTable:
line 6: 0 //java中的第6行,对比上面的0
line 7: 2 java中的第7行,对比上面的2
line 8: 5
line 9: 8
line 10: 15
line 11: 23
//本地变量
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 args [Ljava/lang/String;
2 22 1 a I
5 19 2 b I
8 16 3 c I
15 9 4 d I