详解 JVM 字节码(2)
2019-05-03 本文已影响12人
zidea
jvm
这张表中是按顺序指出我们字节码中内容。
什么是字节码,为什么需要字节码
编译 JVM 编译成机器码,
我们看一看 Java 编译过程
- java 源码
- 编译为 class 文件
JVM 接收字节码文件后对其进行校验
验证字节码文件正确性然后通过类加载器加载生成 class 类来运行
【图】
为什么学习字节码?
大家都知道 java 文件编译后为二进制的字节码文件可以被 JVM 读懂,学习好字节码文件可以让我们对 Java 这门语言有深入了解。只要遵循 JVM 规范你就可以创建出自己语言。可以反编译一些第三方库。
java 规范分为两种
- java 语言本身的规范
- JVM 的规范。字节码属于 JVM 规范。JVM 只是关心字节码
这里我们整个字节码分析都会以整个类文件,通过读整个类编译后的字节码来学习字节码。
package com.zidea;
public class Tut {
private String title = "basic";
private int courses = 10;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getCourses() {
return courses;
}
public void setCourses(int courses) {
this.courses = courses;
}
}
使用 javap 命令查看编译好的 Tut.class 文件
javap Tut.class
public class com.zidea.Tut {
public com.zidea.Tut();
public java.lang.String getTitle();
public void setTitle(java.lang.String);
public int getCourses();
public void setCourses(int);
}
javap -c Tut.class
用命令查看 class 文件我们可以查看到许多助记符。
javap -verbose Tut.class
jangwoodeMacBook-Air:zidea jangwoo$ javap -c Tut.class
Compiled from "Tut.java"
public class com.zidea.Tut {
public com.zidea.Tut();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String basic
7: putfield #3 // Field title:Ljava/lang/String;
10: aload_0
11: bipush 10
13: putfield #4 // Field courses:I
16: return
public java.lang.String getTitle();
Code:
0: aload_0
1: getfield #3 // Field title:Ljava/lang/String;
4: areturn
public void setTitle(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #3 // Field title:Ljava/lang/String;
5: return
public int getCourses();
Code:
0: aload_0
1: getfield #4 // Field courses:I
4: ireturn
public void setCourses(int);
Code:
0: aload_0
1: iload_1
2: putfield #4 // Field courses:I
5: return
}
javap -verbose 命令来分析字节码文件
- 魔数
- 版本号
- 常量池
- 类信息 ,class 信息不会丢失在进入 JVM
- 类的构造方法
- 类中的方法信息
- 类变量与成员变量等信息
Classfile /Users/jangwoo/IdeaProjects/jvm_demo/out/production/jvm_demo/com/zidea/Tut.class
Last modified May 2, 2019; size 746 bytes
MD5 checksum e58553db6eb469ec2411f8711ace986a
Compiled from "Tut.java"
public class com.zidea.Tut
minor version: 0
major version: 54
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // com/zidea/Tut
super_class: #6 // java/lang/Object
interfaces: 0, fields: 2, methods: 5, attributes: 1
Constant pool:
#1 = Methodref #6.#28 // java/lang/Object."<init>":()V
#2 = String #29 // basic
#3 = Fieldref #5.#30 // com/zidea/Tut.title:Ljava/lang/String;
#4 = Fieldref #5.#31 // com/zidea/Tut.courses:I
#5 = Class #32 // com/zidea/Tut
#6 = Class #33 // java/lang/Object
#7 = Utf8 title
#8 = Utf8 Ljava/lang/String;
#9 = Utf8 courses
#10 = Utf8 I
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcom/zidea/Tut;
#18 = Utf8 getTitle
#19 = Utf8 ()Ljava/lang/String;
#20 = Utf8 setTitle
#21 = Utf8 (Ljava/lang/String;)V
#22 = Utf8 getCourses
#23 = Utf8 ()I
#24 = Utf8 setCourses
#25 = Utf8 (I)V
#26 = Utf8 SourceFile
#27 = Utf8 Tut.java
#28 = NameAndType #11:#12 // "<init>":()V
#29 = Utf8 basic
#30 = NameAndType #7:#8 // title:Ljava/lang/String;
#31 = NameAndType #9:#10 // courses:I
#32 = Utf8 com/zidea/Tut
#33 = Utf8 java/lang/Object
{
public com.zidea.Tut();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String basic
7: putfield #3 // Field title:Ljava/lang/String;
10: aload_0
11: bipush 10
13: putfield #4 // Field courses:I
16: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 10
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 this Lcom/zidea/Tut;
public java.lang.String getTitle();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #3 // Field title:Ljava/lang/String;
4: areturn
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zidea/Tut;
public void setTitle(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #3 // Field title:Ljava/lang/String;
5: return
LineNumberTable:
line 12: 0
line 13: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/zidea/Tut;
0 6 1 title Ljava/lang/String;
public int getCourses();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #4 // Field courses:I
4: ireturn
LineNumberTable:
line 16: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zidea/Tut;
public void setCourses(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #4 // Field courses:I
5: return
LineNumberTable:
line 20: 0
line 21: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/zidea/Tut;
0 6 1 courses I
}
SourceFile: "Tut.java"
这里我们按顺序了解一下 java 字节码是由哪些部分组成,然后对照创建 Tut
的字节码文件进行分析
这张表中是按顺序指出我们字节码中内容。
java 字节码中有两种数据类型
- 字节数据直接量:为基本的数据类型,例如 u1、u2、u4 和 u8,分别代表连续的 1 字节、2 字节、4字节和 8 字节组成整体数据。
- 表(数组):表是由多个基本数据或其他表,按照既定顺序组成的数据集合。例如常量池就是表。
其中表长度是不固定的,他是元素类型(不同类型有不同内容)和元素数量决定。
魔数
所有 class 字节码文件的前 4 个字节都是魔数,魔数值为固定值CA FE BA BE
屏幕快照 2019-05-02 下午8.36.59.png单个字节,CA 表示一个字节,C 是 16 进制由 4 位来表示16进制,CA 两个在一起表示 8 位。JVM 编译出来的字节码文件是以 8 位字节为最小单位构成。