一:class文件概述
Class文件是JVM的输入,Java虚拟机规范中定义了Class文件的结构。Class文件是JVM实现平台
无关、技术无关的基础。
1:Class文件是一组以8字节为单位的字节流,各个数据项目按顺序紧凑排列
2:对于占用空间大于8字节的数据项,按照高位在前的方式分割成多个8字节进行存储
3:Class文件格式里面只有两种类型:无符号数、表
无符号数:基本数据类型,以u1、u2、u4、u8来代表几个字节的无符号数
表:由多个无符号数和其它表构成的复合数据类型,通常以”_info”结尾
二:ClassFile结构
参看规范4.1 , 同时用 javap –v(verbose)来查看字节码文件
每个class对应一个下图所示的ClassFile结构:
- magic
魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的 Class 文件。魔数值固定为 0xCAFEBABE,不会改变。
- minor_version、major_version
副版本号和主版本号,minor_version 和 major_version 的值分别表示 Class 文件的副、主版本。它们共同构成了 Class 文件的格式版本号。 - constant_pool_count
常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。constant_pool 表的索引值只有在大于 0 且小于 constant_pool_count 时才会被
认为是有效的
- constant_pool[ ]
常量池,constant_pool 是一种表结构(这里需要列举一下表就会明白,这个在下面的例子中会有讲解这个结构,返回来在读就会明白),它包含 Class 文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池中的每一项都具备相同的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tagbyte”。常量池的索引范围是 1 至 constant_pool_count−1。
常量池的项目类型
注意:常量池的常量结构请参看java虚拟机规范4.4章节。
- access_flags
访问标志,access_flags 是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags 的取值范围和相应含义见表 4.1 所示。
带有 ACC_SYNTHETIC 标志的类,意味着它是由编译器自己产生的而不是由程序员编写的源代码生成的。
带有 ACC_ENUM 标志的类,意味着它或它的父类被声明为枚举类型。
带有 ACC_INTERFACE 标志的类,意味着它是接口而不是类,反之是类而不是接口。如果一个 Class 文件被设置了 ACC_INTERFACE 标志,那么同时也得设置ACC_ABSTRACT 标志(JLS §9.1.1.1)。同时它不能再设置 ACC_FINAL、ACC_SUPER 和 ACC_ENUM 标志。
注解类型必定带有 ACC_ANNOTATION 标记,如果设置了 ANNOTATION 标记,ACC_INTERFACE 也必须被同时设置。如果没有同时设置 ACC_INTERFACE 标记,那么这个Class文件可以具有表4.1中的除ACC_ANNOTATION外的所有其它标记。当然 ACC_FINAL 和 ACC_ABSTRACT 这类互斥的标记除外(JLS §8.1.1.2)。
ACC_SUPER 标志用于确定该 Class 文件里面的 invokespecial 指令使用的是哪一种执行语义。目前 Java 虚拟机的编译器都应当设置这个标志。ACC_SUPER 标记是为了向后兼容旧编译器编译的 Class 文件而存在的,在 JDK1.0.2 版本以前的编译器产生的 Class 文件中,access_flag 里面没有 ACC_SUPER 标志。同时,JDK1.0.2 前的 Java 虚拟机遇到 ACC_SUPER 标记会自动忽略它。
在表 4.1 中没有使用的 access_flags 标志位是为未来扩充而预留的,这些预留的标志为在编译器中会被设置为 0, Java 虚拟机实现也会自动忽略它们。
- this_class
类索引,this_class 的值必须是对 constant_pool 表中项目的一个有效索引值。constant_pool 表在这个索引处的项必须为 CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类或接口。 - super_class
父类索引,对于类来说,super_class 的值必须为 0 或者是对 constant_pool 表中项目的一个有效索引值。如果它的值不为 0,那 constant_pool 表在这个索引处的项
必须为 CONSTANT_Class_info 类型常量(§4.4.1),表示这个 Class 文件所定义的类的直接父类。当前类的直接父类,以及它所有间接父类的 access_flag 中都不能带有 ACC_FINAL 标记。对于接口来说,它的 Class 文件的 super_class 项的值必须是对 constant_pool 表中项目的一个有效索引值。constant_pool 表在这个索引处的
项必须为代表 java.lang.Object 的 CONSTANT_Class_info 类型常量(§4.4.1)。如果 Class 文件的 super_class 的值为 0,那这个 Class 文件只可能是定义的是
java.lang.Object 类,只有它是唯一没有父类的类。
-
interfaces_count
接口计数器,interfaces_count 的值表示当前类或接口的直接父接口数量。 -
interfaces[]
接口表,interfaces[]数组中的每个成员的值必须是一个对 constant_pool 表中项目的一个有效索引值,它的长度为 interfaces_count。每个成员 interfaces[i] 必须为 CONSTANT_Class_info 类型常量(§4.4.1),其中 0 ≤ i <interfaces_count。在 interfaces[]数组中,成员所表示的接口顺序和对应的源代码中给定的接口顺序(从左至右)一样,即 interfaces[0]对应的是源代码中最左边的接口。 -
fields_count
字段计数器,fields_count 的值表示当前 Class 文件 fields[]数组的成员个数。fields[]数组中每一项都是一个 field_info 结构(§4.5)的数据项,它用于表示该类或接口声明的类字段或者实例字段 。 -
fields[]
字段表,fields[]数组中的每个成员都必须是一个 fields_info 结构(§4.5)的数据项,用于表示当前类或接口中某个字段的完整描述。fields[]数组描述当前类或接口
声明的所有字段,但不包括从父类或父接口继承的部分。 -
methods_count
方法计数器,methods_count 的值表示当前 Class 文件 methods[]数组的成员个数。Methods[]数组中每一项都是一个 method_info 结构(§4.5)的数据项。 -
attributes_count
属性计数器,attributes_count 的值表示当前 Class 文件 attributes 表的成员个数。attributes 表中每一项都是一个 attribute_info 结构(§4.7)的数据项。 -
attributes[]
属性表,attributes 表的每个项的值必须是 attribute_info 结构(§4.7)。在本规范里,Class 文件结构中的 attributes 表的项包括下列定义的属性:
三:ClassFile实例分析
1.新建java类代码如下:
public class Main {
public static void main(String[] args) {
int a = 1000 ,b = 2000,c =3000 ,d = 4000 ,e = 5000 ,f = 6000;
int h = a + c + d + e + f/b;
if(h>1000){
System.out.println("h>1000");
}
StringBuffer stringBuffer = new StringBuffer("JVMTeset");
int[] y= new int[]{1,2,3,4,5};
System.out.println(h);
System.out.println(stringBuffer.toString());
}
}
2.class文件代码:
3.编译代码得到class文件,javap -v Main.calss 查看字节码文件。如下:
D:\>javap -v Main.class
Classfile /D:/Main.class
Last modified 2018-11-8; size 979 bytes
MD5 checksum 2311f729f8b77e197a691f5cb20f4f03
Compiled from "Main.java"
public class Main
SourceFile: "Main.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#39 // java/lang/Object."<init>":()V
#2 = Fieldref #40.#41 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #42 // h>1000
#4 = Methodref #43.#44 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #45 // java/lang/StringBuffer
#6 = String #46 // JVMTeset
#7 = Methodref #5.#47 // java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
#8 = Methodref #43.#48 // java/io/PrintStream.println:(I)V
#9 = Methodref #5.#49 // java/lang/StringBuffer.toString:()Ljava/lang/String;
#10 = Class #50 // Main
#11 = Class #51 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 LMain;
#19 = Utf8 main
#20 = Utf8 ([Ljava/lang/String;)V
#21 = Utf8 args
#22 = Utf8 [Ljava/lang/String;
#23 = Utf8 a
#24 = Utf8 I
#25 = Utf8 b
#26 = Utf8 c
#27 = Utf8 d
#28 = Utf8 e
#29 = Utf8 f
#30 = Utf8 h
#31 = Utf8 stringBuffer
#32 = Utf8 Ljava/lang/StringBuffer;
#33 = Utf8 y
#34 = Utf8 [I
#35 = Utf8 StackMapTable
#36 = Class #22 // "[Ljava/lang/String;"
#37 = Utf8 SourceFile
#38 = Utf8 Main.java
#39 = NameAndType #12:#13 // "<init>":()V
#40 = Class #52 // java/lang/System
#41 = NameAndType #53:#54 // out:Ljava/io/PrintStream;
#42 = Utf8 h>1000
#43 = Class #55 // java/io/PrintStream
#44 = NameAndType #56:#57 // println:(Ljava/lang/String;)V
#45 = Utf8 java/lang/StringBuffer
#46 = Utf8 JVMTeset
#47 = NameAndType #12:#57 // "<init>":(Ljava/lang/String;)V
#48 = NameAndType #56:#58 // println:(I)V
#49 = NameAndType #59:#60 // toString:()Ljava/lang/String;
#50 = Utf8 Main
#51 = Utf8 java/lang/Object
#52 = Utf8 java/lang/System
#53 = Utf8 out
#54 = Utf8 Ljava/io/PrintStream;
#55 = Utf8 java/io/PrintStream
#56 = Utf8 println
#57 = Utf8 (Ljava/lang/String;)V
#58 = Utf8 (I)V
#59 = Utf8 toString
#60 = Utf8 ()Ljava/lang/String;
{
public Main();
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 LMain;
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=10, args_size=1 //stack:方法执行时,操作栈的深度
//Locals:局部变量所需的存储空间,单位是slot
//slot是虚拟机为局部变量分配内存所使用的最小单位
//args_size:参数个数,为1的话,因为实例方法默认会传入this,locals也会预留一个
//slot来存放
0: sipush 1000
3: istore_1
4: sipush 2000
7: istore_2
8: sipush 3000
11: istore_3
12: sipush 4000
15: istore 4
17: sipush 5000
20: istore 5
22: sipush 6000
25: istore 6
27: iload_1
28: iload_3
29: iadd
30: iload 4
32: iadd
33: iload 5
35: iadd
36: iload 6
38: iload_2
39: idiv
40: iadd
41: istore 7
43: iload 7
45: sipush 1000
48: if_icmple 59
51: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
54: ldc #3 // String h>1000
56: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: new #5 // class java/lang/StringBuffer
62: dup
63: ldc #6 // String JVMTeset
65: invokespecial #7 // Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
68: astore 8
70: iconst_5
71: newarray int
73: dup
74: iconst_0
75: bipush 11
77: iastore
78: dup
79: iconst_1
80: bipush 22
82: iastore
83: dup
84: iconst_2
85: bipush 33
87: iastore
88: dup
89: iconst_3
90: bipush 44
92: iastore
93: dup
94: iconst_4
95: bipush 55
97: iastore
98: astore 9
100: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
103: iload 7
105: invokevirtual #8 // Method java/io/PrintStream.println:(I)V
108: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
111: aload 8
113: invokevirtual #9 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
116: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
119: return
LineNumberTable:
line 4: 0
line 5: 27
line 6: 43
line 7: 51
line 9: 59
line 10: 70
line 11: 100
line 12: 108
line 13: 119
LocalVariableTable:
Start Length Slot Name Signature
0 120 0 args [Ljava/lang/String;
4 116 1 a I
8 112 2 b I
12 108 3 c I
17 103 4 d I
22 98 5 e I
27 93 6 f I
43 77 7 h I
70 50 8 stringBuffer Ljava/lang/StringBuffer;
100 20 9 y [I
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 59
locals = [ class "[Ljava/lang/String;", int, int, int, int, int, int, int ]
stack = []
以上是编译后的class文件和字节码文件信息,具体内容请参照 java虚拟机规范