JVM 类加载与字节码技术(1)《类文件结构》

HelloWorld.java

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

执行编译 javac -parameters -d . HelloWworld.java,如果加-parameters这个参数,那么他就会保留方法中参数的名称信息(比如 args)。

编译为 HelloWorld.class 后是这个样子的,比如在Linux下用od -t xC HelloWorld.class 遍历查看二进制字节码文件内容:

[root@localhost ~]# od -t xC HelloWorld.class
0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09
0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07
0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
0000120 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
0000140 00 04 74 68 69 73 01 00 1d 4c 63 6e 2f 69 74 63
0000160 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c 6f
0000200 57 6f 72 6c 64 3d 01 00 04 6d 61 69 6e 01 00 16
0000220 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72
0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13
0000260 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69
0000300 6e 67 3b 01 00 10 4d 65 74 68 6f 64 50 61 72 61
0000320 6d 65 74 65 72 73 01 00 0a 53 6f 75 72 63 65 46
0000340 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64
0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e
0000400 00 1f 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64
0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74
0000440 63 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c
0000460 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61
0000500 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61
0000520 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f 
0000540 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72
0000560 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76
0000600 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d
0000620 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a
0000640 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b
0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01
0000700 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01
0000720 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00
0000740 00 00 06 00 01 00 00 00 04 00 0b 00 00 00 0c 00
0000760 01 00 00 00 05 00 0c 00 0d 00 00 00 09 00 0e 00
0001000 0f 00 02 00 09 00 00 00 37 00 02 00 01 00 00 00
0001020 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 02 00 0a
0001040 00 00 00 0a 00 02 00 00 00 06 00 08 00 07 00 0b
0001060 00 00 00 0c 00 01 00 00 00 09 00 10 00 11 00 00 
0001100 00 12 00 00 00 05 01 00 10 00 00 00 01 00 13 00
0001120 00 00 02 00 14

比如第一列是标号(8进制),后面的ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09以及下面的都是字节码内容。

根据JVM规范(比如各个厂商实现虚拟机时必须遵守这个规范),类文件结构如下:

ClassFile {
	u4		magic;
	u2		minor_version;
	u2		major_version;
	u2		constant_pool_count;
	cp_info		constant_pool[constant_pool_count-1];
	u2 		access_flags;
	u2		this_class;
	u2		super_class;
	u2		interfaces_count;
	u2		interfaces[interfaces_count];
	u2		fields_count;
	field_info	field[fields_count];
	u2		methods_count;
	method_info	methods[methods_count];
	u2		attributes_count;
	attribute_info	attributes[attributes_count];
}

可以看到前面的u2 u4这样的是字节数,比如前四个字节(u4)是魔术magic,接下来的两个字节(u2)是小版本号minor_version,再接下来两个字节(u2)是主版本号major_version等等。

还有下面的constant_pool_count;constant_pool[constant_pool_count-1];常量池的信息,access_flags是访问修饰(比如这个类是公共的还是私有的),this_class是这个类自己的包名、类名的信息,super_class是父类的信息,interfaces_countinterfaces[interfaces_count]是指他的接口信息,fields_countfield[fields_count]是类中的变量信息,methods_countmethods[methods_count]是类中的方法信息,attributes_count;attributes[attributes_count]是指类中的一些附加信息。

魔术

0~3字节,表示他是否是【class】类型的文件。

0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09

其中的ca fe ba be是魔术代表的四个字节(0~3),魔术是指所有的文件他都有自己的特定的类型,比如Java文件他用了四个字节来表示我这是一个Java文件,而不是其他的文件,不同的文件他有自己的魔术信息,比如你打开一个png的图片和一个jpg的图片他们的魔术信息是不一样的,即魔术就是标识你这个文件到底是啥类型。Java的魔术就是ca fe ba be咖啡宝贝,算是Java的冷幽默)。

版本

0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09

接下来的四个字节 00 00 00 34 代表的是版本。前两个00 00minor version,好像没有在类文件中体现,主要是他的主版本即00 3434是16进制,翻译成十进制就是52),52就代表JDK8,51是JDK7,53是JDK9。总之,编译后的class文件版本是34的话,就说明是基于JDK8的版本。

常量池

【表1】

Contant TypeValue
CONSTANT_Class7
CONSTANT_Fieldref9
CONSTANT_Methodref10
CONSTANT_InterfaceMethodref11
CONSTANT_String8
CONSTANT_Integer3
CONSTANT_Float4
CONSTANT_Long5
CONSTANT_Double6
CONSTANT_NameAndType12
CONSTANT_Utf81
CONSTANT_MethodHandle15
CONSTANT_MethodType16
CONSTANT_InvokeDynamic18

0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09

常量池占据了整个类文件中的相当大的比重。8 ~ 9字节,即00 23表示常量池长度,00 23(35)表示常量池有 #1~#34项(一共有35项),注意#0项不计入(所以有34项),也没有值。

详细如下

第 #1 项
0a 表示一个method信息,00 0600 15(21)表示他引用了常量池中#6#21项来获得这个方法的【所属类】和【方法名】

0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09

第 #2 项
09 表示一个filed信息,00 16(22)和00 17(23)表示他引用了常量池中#22#23项来获得这个成员变量的【所属类】和【成员变量名】

0000000 ca fe ba be 00 00 00 34 00 23 0a 00 06 00 15 09
0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07

第 #3 项
08 表示一个字符串常量名称,00 18(24)表示它引用了常量池中 #24

0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07

第 #4 项
0a 表示一个method信息,00 19(25)和00 1a(26)表示它引用了常量池中#25#26项来获得这个方法的【所属类】和【方法名】

0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07

第 #5 项
07 表示一个class信息,00 1b(27)表示它引用了常量池中#27

0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07

第 #6 项
07表示一个class信息,00 1c(28)表示它引用了常量池中#28

0000020 00 16 00 17 08 00 18 0a 00 19 00 1a 07 00 1b 07
0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29

第 #07 项
01表示一个utf8串,00 06 表示长度,3c 69 63 69 74 3e是【< init >】

0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29

第 #08 项
01表示一个utf8串,00 03 表示长度,28 29 56 是【()V】其实就是表示无参、无返回值

0000040 00 1c 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e

第 #09 项
01表示一个utf8串,00 04表示长度,43 6f 64 65是【Code(方法的一个属性,对应着字节码)】

0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e

第 #10 项
01 表示一个utf8串,00 0f(15)表示长度,4c 69 6e 65 4e 75 6d 62 72 54 61 62 6c 65是【LineNumberTable(方法的属性,方法的行号表)】

0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63

第 #11 项
01表示一个utf8串,00 12(18)表示长度,4c ~ 65 是【LocalVariableTable(代表方法的一个属性,是方法的本地变量表)】

0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
0000120 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01

第 #12 项
01 表示 utf8串,00 04表示长度,74 68 69 73是【this】

0000120 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
0000140 00 04 74 68 69 73 01 00 1d 4c 63 6e 2f 69 74 63

第 #13 项
01表示utf8串,00 1d(29)表示长度,是【Lcom/waca/jvm/t5/HelloWorld;(比如)(这是类型,在字节码里表示类型的话,引用类型是L开头;结尾)】

0000140 00 04 74 68 69 73 01 00 1d 4c 63 6e 2f 69 74 63
0000160 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c 6f
0000200 57 6f 72 6c 64 3d 01 00 04 6d 61 69 6e 01 00 16

第 #14 项
01表示 utf8串,00 04表示长度,74 68 69 73 是【main(方法名称,可以和#13联系起来,是表示哪个类中的main方法)】

0000200 57 6f 72 6c 64 3d 01 00 04 6d 61 69 6e 01 00 16

第 #15 项
01表示一个utf8串,00 16(22)表示长度,是【([Ljava/lang/String;)V】括号里面是参数类型,前面再加一个左半边的[,表示他是数组,即其实就是参数为字符串数组,无返回值。

0000200 57 6f 72 6c 64 3d 01 00 04 6d 61 69 6e 01 00 16
0000220 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72
0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13

第 #16 项
01表示一个utf8串,00 04表示长度,是【args(刚才main方法的参数名称)】

0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13

第 #17 项
01表示一个utf8串,00 13(19)表示长度,是【[Ljava/lang/String;(进一步描述了刚才的参数类型)】

0000240 69 6e 67 3b 29 56 01 00 04 61 72 67 73 01 00 13
0000260 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69
0000300 6e 67 3b 01 00 10 4d 65 74 68 6f 64 50 61 72 61

第 #18 项
01 表示utf8串,00 10(16)表示长度,是【MethodParameters(方法的一个属性,是指方法的参数名称信息)】

0000300 6e 67 3b 01 00 10 4d 65 74 68 6f 64 50 61 72 61
0000320 6d 65 74 65 72 73 01 00 0a 53 6f 75 72 63 65 46

第 #19 项
01表示一个utf8串,00 0a(10)表示长度,是【SourceFile】

0000320 6d 65 74 65 72 73 01 00 0a 53 6f 75 72 63 65 46
0000340 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64

第 #20 项
01表示一个utf8串,00 0f(15)表示长度,是【HelloWorld.java(#19 #20联系起来就是说这个类的源文件是HelloWorld.java)】

0000340 69 6c 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64
0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e

第 #21 项
0c(12)表示一个【名+类型】,00 07 00 08引用了常量池中#7 #8两项。

0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e

第 #22 项
07 表示一个class信息,00 1d(29)引用了常量池中#29

0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e

第 #23 项
0c表示一个【名+类型】,00 1e(30)00 1f(31)引用了常量池中#30 #31两项。

0000360 2e 6a 61 76 61 0c 00 07 00 08 07 00 1d 0c 00 1e
0000400 00 1f 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64

第 #24 项
01表示一个utf8串,00 0b(11)表示长度,是hello world

0000400 00 1f 01 00 0b 68 65 6c 6c 6f 20 77 6f 72 6c 64

第 #25 项
07表示一个class信息,00 20(32)引用了常量池中#32

0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74

第 #26 项
0c表示【名+类型】,00 21(33)00 22(34)引用了常量池中#33 #34两项

0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74

第 #27 项
01表示一个utf8串,00 1b(27)表示长度,是【com/waca/jvm/t5/HelloWorld(比如)】

0000420 07 00 20 0c 00 21 00 22 01 00 1b 63 6e 2f 69 74
0000440 63 61 73 74 2f 6a 76 6d 2f 74 35 2f 48 65 6c 6c
0000460 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61

第 #28 项
01 表示一个utf8串,00 10(16)表示长度(6a开始到74,即就是Object类),是【java/lang/Object】

0000460 6f 57 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61
0000500 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61

第 #29 项
01 表示一个utf8串,00 10(16)表示长度,即6a到6d,是java/lang/System

0000500 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61
0000520 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f

第 #30 项
01 表示一个utf8串,00 03表示长度,是out

0000520 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f
0000540 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72

第 #31 项
01表示一个utf8串,00 15(21)表示长度,即4c到3b,是Ljava/io/PrintStream;

0000540 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72
0000560 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76

第 #32 项
01 表示一个utf8串,00 13(19)表示长度,是java/io/PrintStream

0000560 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76
0000600 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d

第 #33 项
01 表示一个utf8串,00 07表示长度,是【println】

0000620 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a

第 #34 项
01 是utf8串,00 15(21)是长度,是【(Ljava/lang/String;)V】,即类型是String类型,返回值是void

0000620 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a
0000640 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b
0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01

通过这个小例子,可以看得出常量池里就是记录着我们Java类中的各种信息,包括它的类的信息父类的信息方法的信息成员变量的信息等等,还有方法的属性

访问标识和继承信息

上面的是类文件结构中的魔术版本常量池,接下来是access_flags,这是访问修饰的描述。

0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01

29 56为止是常量池,从00开始是access_flag

00 21表示该class是一个类,公共的(21怎么看呢?要看【表2】中哪几项加起来的,比如表中0x0020加0x0001就是21,表示他是一个公共的类)。

00 05表示根据常量池中去第#5项找到本类全限定名(可以参考为com/waca/HelloWorld.java)。

00 06表示根据常量池中去第#6项找到父类全限定名。(可以参考为java/lang/Object)。

00 00表示接口的数量,本类为0(表示没有接口,如果有接口的话,00 00后面还会有一些描述接口的信息)。

【表2】

FlagNameValueInterpretation
ACC_PUBLIC0x0001Declared public;may be accessed from outside its
ACC_FINAL0x0010Declared final;no subclasses allowed
ACC_SUPER0x0020Treat superclass methods specially when invoked by the invokespecial instruction(表示类)
ACC_INTERFACE0x0200Is an interface,not a class
ACC_ABSTRACT0x0400Declared abstract;must not be instantiated
ACC_SYNTHETIC0x1000Declared synthetic;not present in the source code(人工合成的,不是源代码的)
ACC_ANNOTATION0x2000Declared as an annotation type(注解)
ACC_ENUM0x4000Declared as an enum type(枚举)

field 信息

接下来就是 fields_countfields[fields_count],即成员变量的信息。

0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01

这里的00 00表示成员变量数量是0。

FieldTypeTypeInterpretation
Bbytesigned byte
CcharUnicode character code point in the Basic Multilingual Plane,encoded with UTF-16
Ddoubledouble-precision floating-point value
Ffloatsingle-precision floating-poiint value
Iintinteger
Jlonglong integer
L ClassName;referencean instance of class ClassName
Sshortsigned short
Zbooleantrue or false
[referenceone array dimension(一维数组)

method信息

0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01

00 02两个字节开始,这是代表本类中方法的数量,02说明有两个方法,第一个其实就是那个构造方法,第二个是main方法。一个方法由访问修饰符名称参数描述方法属性数量方法属性组成。

0000660 29 56 00 21 00 05 00 06 00 00 00 00 00 02 00 01
0000700 00 07 00 08 00 01 00 09 00 00 00 2f 00 01 00 01
0000720 00 00 00 05 2a b7 00 01 b1 00 00 00 02 00 0a 00
0000740 00 00 06 00 01 00 00 00 04 00 0b 00 00 00 0c 00
0000760 01 00 00 00 05 00 0c 00 0d 00 00 00 09 00 0e 00

从第一行的最后00 01开始到最后一行的00 00为止,是方法信息。

对构造方法的分析

00 01代表方法的访问修饰(可以参考上面【表2】),01就是公共方法。

接着00 07代表方法的名字,07表示要查询常量池中的#07项来得到方法的名字(查表可知,他是init方法)。

00 08是方法的参数和返回值类型信息,08表示要查询常量池中的#08项来获得这些信息(查表可知,无参、返回值是void)。

00 01是代表方法属性的数量,即有一个属性,具体是什么属性呢,下面的00 09,即要查常量池表中的#09项来得到具体是哪个属性(查表可知,他是code属性,code代表方法体中的代码属性),那么code属性由哪些部分组成?

接下来的00 00 00 2f(47)这四个字节代表整个code属性的长度,即有47个字节长度,往后从00 01...00 00结束为止,这些都是属性的范围,其中00 01(上面的2f之后)是代表方法的一个操作数栈的深度,接下来的00 01代表这个方法的局部变量表的长度,00 00 00 05是具体的code属性内代码的一个长度,即往后数5个字节即 2a b7 00 01 b1就是方法体内的字节码(其实这就是构造方法内部真正要执行的五个字节的代码),再往后00 00 00 02表示比如code属性还会有一些子属性,即00 0a常量池#10项,是LineNumberTable属性,这个属性就是把我们字节码的行号跟Java源码的行号进行一个对应,主要是为了方便以后debug调试来使用的)。00 00 00 06表示LineNumberTable属性的总长度,数6个字节,即00 01 00 00 00 04,这就是第一个子属性,就是行号对应表的属性,其中00 01是表的长度,00 00 代表字节码的jichanghao(?),即2a b7 00 01 b1是第0行,他对应着Java源码中的00 04即第四行(可以翻一下Java源码),这是第一个属性。接下来00 0b...00 00是第二个属性,其中的00 0b也表示查常量池中第#11项(查表可知,是LocalVariableTable属性,即局部变量表),局部变量表整个属性的长度是00 00 00 0c(12),即往后数12个字节,00到最后结束的00为止,其中00 01表示局部变量表里有1个局部变量,00 00 00 05表示从字节码的第0行开始开始到字节码的第5行,即这个局部变量的作用范围涵盖了这个方法内的所有行,从第0行到第5行,00 0c表示局部变量的名字是查常量池中第#12项(查表可知是this),即这是表示构造方法里有一个this的局部变量,类型是00 0d常量池#13项)即是Lcom/waca/HelloWorld,即this是当前这个类的类型,最后的00 00表示他的局部变量表中的槽位号,即第0号(可以看到槽位长度是1,上面提到过“接下来的00 01代表这个方法的局部变量表的长度”),即这个this占用了槽位中第0号。

对main方法的分析

0000760 01 00 00 00 05 00 0c 00 0d 00 00 00 09 00 0e 00
0001000 0f 00 02 00 09 00 00 00 37 00 02 00 01 00 00 00
0001020 09 b2 00 02 12 03 b6 00 04 b1 00 00 00 02 00 0a
0001040 00 00 00 0a 00 02 00 00 00 06 00 08 00 07 00 0b
0001060 00 00 00 0c 00 01 00 00 00 09 00 10 00 11 00 00

00 09代表方法的访问修饰符(比public多出了8,即static,所以这是public static)。

00 0e是方法的名称(查常量池表就可以知道是main)。

00 0f表示方法的类型(查常量表可知参数类型是String[] 返回值类型是void)。

00 02是方法属性的个数,这里是2,跟刚才的构造方法相比多出了1个,是因为刚才的构造方法没有参数,而Main方法是有参数,所以他会把这个参数作为一个属性再加入到字节码中。

00 09是第一个属性,查常量池表就能知道是code属性,就是代表了方法体内的那些代码,00 00 00 37是整个code属性的总长度,即00 02到 00 00(0001060的最后),这么长的字节都是这个属性的范围,其中00 02代表操作数栈的最大深度是2,00 01是代表局部变量表的长度,刚才构造方法的局部变量长度是1,里面存了一个this,而这个main方法虽然他里面没有this,但是他多了一个字符串数组的参数,所以他的局部变量表的长度也是1,00 00 00 09是方法体内代码的长度,即从b2b1,这就是主方法内具体的代码,当然我们知道主方法内就打印了System.out.println(“hello world”); 其实就长这样b2 00 02 12 03 b6 00 04 b100 00 00 02是说我有两个附加属性,00 0a是第一个附加属性,查表可知是LineNumberTable属性,即行号表,00 00 00 0a是行号表的长度,即10个字节,从00 02...00 07,行号表里面有两项(00 02),第一项是说00 00 即字节码中第0行对应着Java源代码中第6行(00 06),字节码中第8行(00 08)对应着Java源代码的第7行(00 07)。第二个属性是00 0b,查表可知是局部变量表,他的长度是00 00 00 0c即12,即00 01到结束的00 00,其中00 01表示局部变量里面有1个,他覆盖的范围 00 00 00 09即9个字节,第0行到第9行(b2 00 02 12 03 b6 00 04 b1),这是局部变量的作用范围,00 10 查表可知是他的名字是args,00 11即类型是查常量池
表后可知是string数组类型,最后的00 00表示他占的是哪个槽位,即占的第0行。值得注意的是,目前为止是这个方法的第一个属性,在前面的00 02中可以看到这个方法是有两个属性。

0001100 00 12 00 00 00 05 01 00 10 00 00 00 01 00 13 00

第二个属性是00 12(#18),查常量池表可知是MethodParameters属性,这是因为我们在编译的时候加了一个 -parameters这个选项,他就会编译之后会把这个方法参数的名称信息以MethodParameters编译到字节码中,这样我的运行期间就可以反射得到方法的参数名称了,00 00 00 05表示这个第二个属性的长度是5,然后01 00 10 00 00这五个字节代表什么意思呢,其中01代表参数的数量(因为我们只有一个args参数),00 10 表示参数名字,查常量池知道是args,00 00 表示参数的访问修饰福,这里的00 代表是正常的,如果是10是代表final的,这是第二个方法的分析。

附加属性

0001100 00 12 00 00 00 05 01 00 10 00 00 00 01 00 13 00
0001120 00 00 02 00 14

附加属性的字节是00 01 00 13 00 00 02 00 14,其中00 01表示有1个附加属性,00 13(#19)是表示这个附加属性是SourceFile,就是你这个字节码对应的Java源文件的名称,00 00 00 02表示整个属性的长度是2,所以是最后两个字节00 14(#20),差常量表的话,即可查出来他的源代码是HelloWorld.java这个文件。

参考文件
https://docs.oracel.com/javase/specs/jvms/se8/html/jvms-4.html
通过这个文档都能查询所有的字节描述的含义。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值