手工解读 Java 的 Class 文件(JDK8)

通过手工解析一个简单的 Java 程序所编译的 Class 文件,来了解,学习 Class 文件结构。

阅读目录
  • 一、工具准备
  • 二、开始解读
    • 2.1、代码
      • 2.1.1、Java 源码
      • 2.1.2、class 文件源码
    • 2.2、开始解读
      • 2.2.1 magic
      • 2.2.2 minor_version
      • 2.2.3 major_version
      • 2.2.4 constant_pool_count
      • 2.2.5 constant_pool[constant_pool_count-1]
        • 2.2.5.1 #1
        • 2.2.5.2 #2
        • 2.2.5.3 #3
        • 2.2.5.4 #4
        • 2.2.5.5 #5
        • 2.2.5.6 #6
        • 2.2.5.7 #7
        • 2.2.5.8 #8
        • 2.2.5.9 #9
        • 2.2.5.10 #10
        • 2.2.5.11 #11
        • 2.2.5.12 #12
        • 2.2.5.13 #13
        • 2.2.5.14 #14
        • 2.2.5.15 #15
        • 2.2.5.16 #16
        • 2.2.5.17 #17
        • 2.2.5.18 #18
        • 2.2.5.19 #19
        • 2.2.5.20 #20
        • 2.2.5.21 #21
      • 2.2.6 access_flags
      • 2.2.7 this_class
      • 2.2.8 super_class
      • 2.2.9 interfaces_count
      • 2.2.10 interfaces[interfaces_count]
      • 2.2.11 fields_count
      • 2.2.12 fields[fields_count]
        • 2.2.12.1 #1
      • 2.2.13 methods_count
      • 2.2.14 methods[methods_count]
        • 2.2.14.1 #1
        • 2.2.14.2 #2
      • 2.2.15 attributes_count
      • 2.2.16 attributes[attributes_count]
        • 2.2.16.1 #1
    • 2.3 javap 工具结果
  • 参考

一、工具准备
介绍编译环境和工具。
  • JDK 8
  • IDEA
  • WinHex (查看class文件内容,下载地址:http://www.x-ways.net/winhex/)
  • javap(查看和验证最后结果,JDK自带工具)
  • Java 虚拟机规范 (https://docs.oracle.com/javase/specs/jvms/se8/html/index.html)
二、开始解读 2.1、代码 2.1.1、Java 源码
package com.hyl.learnerJVM.classtest; /** * class测试类 * * @author hyl * @version v1.0: ClassTest.java, v 0.1 2020/8/7 8:03 $ */ public class ClassTest {private long n; public long inLong() { return n + 1; } }

2.1.2、class 文件源码
手工解读 Java 的 Class 文件(JDK8)
文章图片

2.2、开始解读
首先需要了解 class 文件的整体结构。
根据《Java 虚拟机规范》结构如下
ClassFile { u4magic; u2minor_version; u2major_version; u2constant_pool_count; cp_infoconstant_pool[constant_pool_count-1]; u2access_flags; u2this_class; u2super_class; u2interfaces_count; u2interfaces[interfaces_count]; u2fields_count; field_infofields[fields_count]; u2methods_count; method_infomethods[methods_count]; u2attributes_count; attribute_info attributes[attributes_count]; }

来自 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
接下来按照这个结构顺序解读。
2.2.1 magic
u4:4 个字节
Offset0123456789 10 11 12 13 14 15 00000000CA FE BA BE

魔数(Magic Number),唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。(“cafe babe” 和 “Java” 也算是个彩蛋)
官方说明:
The magic item supplies the magic number identifying the class file format; it has the value 0xCAFEBABE.
2.2.2 minor_version
u2:2 个字节
Offset0123456789 10 11 12 13 14 15 0000000000 00

次版本号,曾经在 Java 2 中出现过,以后,就再也没有被使用过了,基本上固定为零了。
2.2.3 major_version
u2:2 个字节
Offset0123456789 10 11 12 13 14 15 0000000000 34

主版本号,Java 版本从 45 开始,JDK 1.1 以后每个大版本加 1 ,这里 52(0x34)就是 JDK1.8 的意思。
官方说明:
The values of the minor_version and major_version items are the minor and major version numbers of this class file. Together, a major and a minor version number determine the version of the class file format. If a class file has major version number M and minor version number m, we denote the version of its class file format as M.m. Thus, class file format versions may be ordered lexicographically, for example, 1.5 < 2.0 < 2.1.
A Java Virtual Machine implementation can support a class file format of version v if and only if v lies in some contiguous range Mi.0 ≤ v ≤ Mj.m. The release level of the Java SE platform to which a Java Virtual Machine implementation conforms is responsible for determining the range.
Oracle’s Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1. support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive.*
2.2.4 constant_pool_count
【手工解读 Java 的 Class 文件(JDK8)】u2:2 个字节
Offset0123456789 10 11 12 13 14 15 0000000000 16

常量池容量计数值,就是接下来有几个常量。这里是显示有 22(0x16)个,但是实际上只有 21 个,因为 第 0 个预留了,用于 “ 不引用任何一个常量池项目 ”,所以常量池的引用是从 1 开始。对于其他集合类型,一般都是从 0 下标开始的。
官方说明:
The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one. A constant_pool index is considered valid if it is greater than zero and less than constant_pool_count, with the exception for constants of type long and double noted in §4.4.5.
2.2.5 constant_pool[constant_pool_count-1]
cp_info:常量表结构,长度是 constant_pool_count-1。(从上文可知是 21 个)
结构如下:
cp_info { u1 tag; u1 info[]; }

tag 的类型如下:
类型 Constant Type 标志 Value 描述
CONSTANT_Class 7 类和接口的符号引用
CONSTANT_Fieldref 9 字段的符号引用
CONSTANT_Methodref 10 类中方法的符号引用
CONSTANT_InterfaceMethodref 11 接口中方法的符号引用
CONSTANT_String 8 字符串类型字面量
CONSTANT_Integer 3 整型字面量
CONSTANT_Float 4 浮点型字面量
CONSTANT_Long 5 长整型字面量
CONSTANT_Double 6 双精度浮点型字面量
CONSTANT_NameAndType 12 字段或方法的部分符号引用
CONSTANT_Utf8 1 UTF-8 编码的字符串
CONSTANT_MethodHandle 15 表示方法句柄
CONSTANT_MethodType 16 表示方法类型
CONSTANT_InvokeDynamic 18 表示一个动态方法调用
接下逐个常量做解读
2.2.5.1 #1
Offset0123456789 10 11 12 13 14 15 000000000A 00 04 00 12

CONSTANT_Methodref 结构:
CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }

解读:
#1 = Methodref#4.#18

2.2.5.2 #2
Offset0123456789 10 11 12 13 14 15 0000000009 0000001600 03 00 13

CONSTANT_Fieldref 结构:
CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }

解读:
#2 = Fieldref#3.#19

2.2.5.3 #3
Offset0123456789 10 11 12 13 14 15 0000001607 00 14

CONSTANT_Class 结构:
CONSTANT_Class_info { u1 tag; u2 name_index; }

解读:
#3 = Class#20

2.2.5.4 #4
Offset0123456789 10 11 12 13 14 15 000000160700 15

CONSTANT_Class 结构:
CONSTANT_Class_info { u1 tag; u2 name_index; }

解读:
#4 = Class#21

2.2.5.5 #5
Offset0123456789 10 11 12 13 14 15 0000001601 00 01 6E

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#5 = Utf8n

这里 utf-8 使用的是缩写编码。从 \u0001\u007f 之间的字符,使用一个字节(相当于1~127的ASCII码)。从\u0080\u07ff 之间采用两个字节。从 \u0800\uffff 按照普通的 UTF-8 使用三个字节。
2.2.5.6 #6
Offset0123456789 10 11 12 13 14 15 0000001601 00 0000003201 4A

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#6 = Utf8J

2.2.5.7 #7
Offset0123456789 10 11 12 13 14 15 0000003201 00 06 3C 69 6E69 74 3E

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#7 = Utf8

2.2.5.8 #8
Offset0123456789 10 11 12 13 14 15 0000003201 00 03 28 29 0000004856

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#8 = Utf8()V

2.2.5.9 #9
Offset0123456789 10 11 12 13 14 15 0000004801 00 04 43 6F 64 65

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#9 = Utf8Code

2.2.5.10 #10
Offset0123456789 10 11 12 13 14 15 0000004801 00 0F 4C 69 6E 65 4E 0000006475 6D 62 65 72 54 61 626C 65

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#10 = Utf8LineNumberTable

2.2.5.11 #11
Offset0123456789 10 11 12 13 14 15 0000006401 00 12 4C 6F 63 0000008061 6C 56 61 72 69 61 626C 65 54 61 62 6C 65

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#11 = Utf8LocalVariableTable

2.2.5.12 #12
Offset0123456789 10 11 12 13 14 15 0000008001 0000009600 04 74 68 69 73

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#12 = Utf8this

2.2.5.13 #13
Offset0123456789 10 11 12 13 14 15 0000009601 0028 4C 63 6F 6D 2F 68 79 000001126C 2F 6C 65 61 72 6E 6572 4A 56 4D 2F 63 6C 61 0000012873 73 74 65 73 74 2F 436C 61 73 73 54 65 73 74 000001443B

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#13 = Utf8Lcom/hyl/learnerJVM/classtest/ClassTest;

2.2.5.14 #14
Offset0123456789 10 11 12 13 14 15 0000014401 00 06 69 6E 4C 6F6E 67

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#14 = Utf8inLong

2.2.5.15 #15
Offset0123456789 10 11 12 13 14 15 0000014401 00 03 28 29 4A

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#15 = Utf8()J

2.2.5.16 #16
Offset0123456789 10 11 12 13 14 15 0000016001 00 0A 53 6F 75 72 6365 46 69 6C 65

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#16 = Utf8SourceFile

2.2.5.17 #17
Offset0123456789 10 11 12 13 14 15 0000016001 00 0E 0000017643 6C 61 73 73 54 65 7374 2E 6A 61 76 61

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#17 = Utf8ClassTest.java

2.2.5.18 #18
Offset0123456789 10 11 12 13 14 15 000001760C 00 0000019207 00 08

CONSTANT_NameAndType 结构:
CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }

解读:
#18 = NameAndType#7:#8

2.2.5.19 #19
Offset0123456789 10 11 12 13 14 15 000001920C 00 05 00 06

CONSTANT_NameAndType 结构:
CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }

解读:
#19 = NameAndType#5:#6

2.2.5.20 #20
Offset0123456789 10 11 12 13 14 15 0000019201 00 26 63 6F 6D 2F 68 0000020879 6C 2F 6C 65 61 72 6E65 72 4A 56 4D 2F 63 6C 0000022461 73 73 74 65 73 74 2F43 6C 61 73 73 54 65 73 0000024074

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#20 = Utf8com/hyl/learnerJVM/classtest/ClassTest

2.2.5.21 #21
Offset0123456789 10 11 12 13 14 15 0000024001 00 10 6A 61 76 612F 6C 61 6E 67 2F 4F 62 000002566A 65 63 74

CONSTANT_Utf8 结构:
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }

解读:
#21 = Utf8java/lang/Object

2.2.6 access_flags
u2:两个字节
Offset0123456789 10 11 12 13 14 15 0000025600 21

访问标志,识别一些类或接口的访问信息,包括,是类还是接口;是否是 public ;是否是 abstract 类型;是否声明 final ;等等。
访问标识如下:
Flag Name Value 含义 Interpretation
ACC_PUBLIC 0x0001 是否为public 类型 Declared public; may be accessed from outside its package.
ACC_FINAL 0x0010 是否被声明 final,只有类可设置 Declared final; no subclasses allowed.
ACC_SUPER 0x0020 是否允许使用invokespecial 字节码指令的新语义 Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE 0x0200 标识这是个接口 Is an interface, not a class.
ACC_ABSTRACT 0x0400 是否为 abstract 类型 Declared abstract; must not be instantiated.
ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码产生的 Declared synthetic; not present in the source code.
ACC_ANNOTATION 0x2000 标识这是个注解 Declared as an annotation type.
ACC_ENUM 0x4000 标识这是个枚举 Declared as an enum type.
原文 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1-200-E.1
解读:
access_flags 的值为:0x0001 | 0x0020 = 0x0021
flags: ACC_PUBLIC, ACC_SUPER

2.2.7 this_class
u2:两个字节
Offset0123456789 10 11 12 13 14 15 0000025600 03

类索引,确定这个类的全限名。指向一个 class 类型的常量。
官方说明:
The value of the this_class item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure (§4.4.1) representing the class or interface defined by this class file.
解读:
#3= Class#20// com/hyl/learnerJVM/classtest/ClassTest

2.2.8 super_class
u2:两个字节
Offset0123456789 10 11 12 13 14 15 0000025600 04

父类索引,确定这个父类的全限名。指向一个 class 类型常量。
官方说明:
For a class, the value of the super_class item either must be zero or must be a valid index into the constant_pool table. If the value of the super_class item is nonzero, the constant_pool entry at that index must be a CONSTANT_Class_info structure representing the direct superclass of the class defined by this class file. Neither the direct superclass nor any of its superclasses may have the ACC_FINAL flag set in the access_flags item of its ClassFile structure.
If the value of the super_class item is zero, then this class file must represent the class Object, the only class or interface without a direct superclass.
For an interface, the value of the super_class item must always be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Class_info structure representing the class Object.
解读:
#4 = Class#21// java/lang/Object

2.2.9 interfaces_count
u2:两个字节
Offset0123456789 10 11 12 13 14 15 0000025600 00

接口计数器,表示接口索引表的容量。
官方说明:
The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type.
解读:
这里接口数量为零,所以接下来的接口索引表不存在了。
2.2.10 interfaces[interfaces_count]
u2:两个字节一组。
因为没有接口,所以这里在 class 文件中不存在。
接口索引表,包含实现的接口。
官方说明:
Each value in the interfaces array must be a valid index into the constant_pool table. The constant_pool entry at each value of interfaces[i], where 0 ≤ i < interfaces_count, must be a CONSTANT_Class_info structure representing an interface that is a direct superinterface of this class or interface type, in the left-to-right order given in the source for the type.
解读:
这里不存在。
2.2.11 fields_count
u2:两个字节。
Offset0123456789 10 11 12 13 14 15 0000025600 01

字段表计数器,表示接下来有几个字段表。
官方说明:
The value of the fields_count item gives the number of field_info structures in the fields table. The field_info structures represent all fields, both class variables and instance variables, declared by this class or interface type.
解读:
接下来有一个字段表。
2.2.12 fields[fields_count]
field_info: 字段表结构,长度是fields_count,这里是一个。
结构如下:
field_info { u2access_flags; u2name_index; u2descriptor_index; u2attributes_count; attribute_info attributes[attributes_count]; }

access_flags,字段修饰符,它与类的修饰符很像都是一个 u2 的数据类型。
字段访问标识含义:
Flag Name Value 含义 Interpretation
ACC_PUBLIC 0x0001 字段是否public Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 字段是否private Declared private; usable only within the defining class.
ACC_PROTECTED 0x0004 字段是否protected Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 字段是否static Declared static.
ACC_FINAL 0x0010 字段是否final Declared final; never directly assigned to after object construction (JLS §17.5).
ACC_VOLATILE 0x0040 字段是否volatile Declared volatile; cannot be cached.
ACC_TRANSIENT 0x0080 字段是否transient Declared transient; not written or read by a persistent object manager.
ACC_SYNTHETIC 0x1000 字段是否由编译器自动产生 Declared synthetic; not present in the source code.
ACC_ENUM 0x4000 字段是否enum Declared as an element of an enum.
descriptor_index,描述符,描述数据类型、方法的参数列表(包括数量、类型以及顺序)和放回值。
描述符标识字符含义:
FieldType term Type 含义 Interpretation
B byte 基本类型byte signed byte
C char 基本类型char Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
D double 基本类型double double-precision floating-point value
F float 基本类型float single-precision floating-point value
I int 基本类型int integer
J long 基本类型long long integer
L ClassName ; reference 对象类型 an instance of class ClassName
S short 基本类型short signed short
Z boolean 基本类型boolean true or false
[ reference 一个数组维度 one array dimension
2.2.12.1 #1
Offset0123456789 10 11 12 13 14 15 0000025600 02 0000027200 05 00 06 00 00

字段 源码 说明
access_flags 00 02 字段是private
name_index 00 05 #5 = Utf8 n字段名为 n
descriptor_index 00 06 #6 = Utf8 J 字段类型为 long
attributes_count 00 00 其他属性描述为空
attributes[attributes_count] - -
解读:
private long n; descriptor: J flags: ACC_PRIVATE

2.2.13 methods_count
u2:两个字节。
Offset0123456789 10 11 12 13 14 15 0000027200 02

方法表集合计数,记录接下里将会有几个方法表集合。
官方说明:
The value of the methods_count item gives the number of method_info structures in the methods table.
解读:
接下来会有两个方法表集合。
2.2.14 methods[methods_count]
method_info:方法表结构,长度methods_count,从上文可知,两个。
结构如下:
method_info { u2access_flags; u2name_index; u2descriptor_index; u2attributes_count; attribute_info attributes[attributes_count]; }

access_flags,方法访问标志。
如下:
Flag Name Value 含义 Interpretation
ACC_PUBLIC 0x0001 方法是否为public Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 方法是否为private Declared private; accessible only within the defining class.
ACC_PROTECTED 0x0004 方法是否为protected Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 方法是否为static Declared static.
ACC_FINAL 0x0010 方法是否为final Declared final; must not be overridden (§5.4.5).
ACC_SYNCHRONIZED 0x0020 方法是否为synchronized Declared synchronized; invocation is wrapped by a monitor use.
ACC_BRIDGE 0x0040 方法是不是由编译器产生的桥接方法 A bridge method, generated by the compiler.
ACC_VARARGS 0x0080 方法是否接受不定参数 Declared with variable number of arguments.
ACC_NATIVE 0x0100 方法是否为native Declared native; implemented in a language other than Java.
ACC_ABSTRACT 0x0400 方法是否为abstract Declared abstract; no implementation is provided.
ACC_STRICT 0x0800 方法是否为strictfp Declared strictfp; floating-point mode is FP-strict.
ACC_SYNTHETIC 0x1000 方法是由编译器自动产生 Declared synthetic; not present in the source code.
attributes,方法属性,以下是属性
Attribute Location class file
Code method_info 45.3
Exceptions method_info 45.3
RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations method_info 49.0
AnnotationDefault method_info 49.0
MethodParameters method_info 52.0
Synthetic ClassFile, field_info, method_info 45.3
Deprecated ClassFile, field_info, method_info 45.3
Signature ClassFile, field_info, method_info 49.0
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations ClassFile, field_info, method_info 49.0
完整属性列表:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7
2.2.14.1 #1
Offset0123456789 10 11 12 13 14 15 0000027200 01 00 07 00 08 00 01 0000028800 09 00 00 00 2F 00 0100 01 00 00 00 05 2A B7 0000030400 01 B1 00 00 00 02 000A 00 00 00 06 00 01 00 0000032000 00 09 00 0B 00 00 000C 00 01 00 00 00 05 00 000003360C 00 0D 00 00

字段对应:
access_flags name_index descriptor_index attributes_count
00 01 00 07 00 08 00 01
ACC_PUBLIC #7 = Utf8 方法名 #8 = Utf8 ()V返回类型viod 有一个属性表集合
方法属性:
0x00 09, #9 = Utf8 Code,是一个Code属性。
Code 属性结构如下:
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; {u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }

对应解读:
类型 长度 源码 含义
attribute_name_index u2 00 09 #9 = Utf8 Code 表明是 Code 属性
attribute_length u4 00 00 00 2F 0x2F,47个长度
max_stack u2 00 01 操作数栈(Operand Stack)深度的最大值,1
max_locals u2 00 01 局部变量表所需的储存空间,1个变量槽(Slot)
code_length u4 00 00 00 05 方法指令长度,5 个字节
code[5]指令解读:
源码 字节码 含义
2A aload_0 将第一引用类型本地变量推送至栈顶
B7 00 01 invokespecial #1 //Method java/lang/Object."":()V 调用超类构造方法,实例初始化方法,私有方法
B1 return 从当前方法返回 viod
exception_table_length,00 00,异常表为零。
exception_table[exception_table_length],就不存在啦。
attributes_count,方法属性表集合数,00 02,两个,如下。
00 0A:
#10 = Utf8 LineNumberTable,可知是LineNumberTable属性。
LineNumberTable属性,用于描述 Java 源码行号与字节码(字节码的偏移量)之间的对应关系。
结构如下:
LineNumberTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 line_number_table_length; {u2 start_pc; u2 line_number; } line_number_table[line_number_table_length]; }

attribute_name_index attribute_length line_number_table_length
00 0A 00 00 00 06 00 01
LineNumberTable 6个字节长度 一个
line_number_table[1]
start_pc start_pc
00 00 00 09
00 0B:
#11 = Utf8 LocalVariableTable,可知是LocalVariableTable 属性。
LocalVariableTable 属性,用于描述栈帧中局部变量表的变量与 Java 源码中定义的变量之间的关系。
结构如下:
LocalVariableTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 local_variable_table_length; {u2 start_pc; u2 length; u2 name_index; u2 descriptor_index; u2 index; } local_variable_table[local_variable_table_length]; }

attribute_name_index attribute_length local_variable_table_length
00 0B 00 00 00 0C 00 01
LocalVariableTable 属性 12个长度 1个值
local_variable_table[1]
start_pc length name_index descriptor_index index
00 00 00 05 00 0C 00 0D 00 00
0 5 #12 = Utf8 this #13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest; 0
start_pclength属性分别代表了整个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖长度,结合起来就是局部变量在字节码之中的作用范围。
this 变量是系统默认添加进去的。
最终整个方法解读为:
public com.hyl.learnerJVM.classtest.ClassTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."":()V 4: return LineNumberTable: line 9: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/hyl/learnerJVM/classtest/ClassTest;

2.2.14.2 #2
Offset0123456789 10 11 12 13 14 15 0000033600 01 000E 00 0F 00 01 00 09 00 0000035200 00 31 00 04 00 01 0000 00 07 2A B4 00 02 0A 0000036861 AD 00 00 00 02 00 0A00 00 00 06 00 01 00 00 0000038400 0E 00 0B 00 00 00 0C00 01 00 00 00 07 00 0C 0000040000 0D 00 00

字段对应:
access_flags name_index descriptor_index attributes_count
00 01 00 0E 00 0F 00 01
ACC_PUBLIC #14 = Utf8 inLong方法名 #15 = Utf8 ()J返回类型 long 有一个属性表集合
方法属性:
0x00 09, #9 = Utf8 Code,是一个Code属性。
Code 属性结构如下:
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; {u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }

对应解读:
类型 长度 源码 含义
attribute_name_index u2 00 09 #9 = Utf8 Code 表明是 Code 属性
attribute_length u4 00 00 00 31 0x31,49个长度
max_stack u2 00 04 操作数栈(Operand Stack)深度的最大值,4
max_locals u2 00 01 局部变量表所需的储存空间,1个变量槽(Slot)
code_length u4 00 00 00 07 方法指令长度,7 个字节
code[7]指令解读:
源码 字节码 含义
2A aload_0 将第一引用类型本地变量推送至栈顶
B4 00 02 getfield #2 // Field n:J 获得指定类的实例域,并将其值压入栈顶
0A lconst_1 将 long 类型 1 推送至栈顶
61 ladd 将栈顶两个 long 类型值相加并将结果压入栈顶
AD lreturn 从当前方法返回 long
exception_table_length,00 00,异常表为零。
exception_table[exception_table_length],就不存在啦。
attributes_count,方法属性表集合数,00 02,两个,如下。
00 0A:
#10 = Utf8 LineNumberTable,可知是LineNumberTable属性。
LineNumberTable属性,用于描述 Java 源码行号与字节码(字节码的偏移量)之间的对应关系。
结构如下:
LineNumberTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 line_number_table_length; {u2 start_pc; u2 line_number; } line_number_table[line_number_table_length]; }

attribute_name_index attribute_length line_number_table_length
00 0A 00 00 00 06 00 01
LineNumberTable 6个字节长度 一个
line_number_table[1]
start_pc start_pc
00 00 00 0E
00 0B:
#11 = Utf8 LocalVariableTable,可知是LocalVariableTable 属性。
LocalVariableTable 属性,用于描述栈帧中局部变量表的变量与 Java 源码中定义的变量之间的关系。
结构如下:
LocalVariableTable_attribute { u2 attribute_name_index; u4 attribute_length; u2 local_variable_table_length; {u2 start_pc; u2 length; u2 name_index; u2 descriptor_index; u2 index; } local_variable_table[local_variable_table_length]; }

attribute_name_index attribute_length local_variable_table_length
00 0B 00 00 00 0C 00 01
LocalVariableTable 属性 12个长度 1个值
local_variable_table[1]
start_pc length name_index descriptor_index index
00 00 00 07 00 0C 00 0D 00 00
0 7 #12 = Utf8 this #13 = Utf8 Lcom/hyl/learnerJVM/classtest/ClassTest; 0
start_pclength属性分别代表了整个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖长度,结合起来就是局部变量在字节码之中的作用范围。
this 变量是系统默认添加进去的。
最终整个方法解读为:
public long inLong(); descriptor: ()J flags: ACC_PUBLIC Code: stack=4, locals=1, args_size=1 0: aload_0 1: getfield#2// Field n:J 4: lconst_1 5: ladd 6: lreturn LineNumberTable: line 14: 0 LocalVariableTable: StartLengthSlotNameSignature 070thisLcom/hyl/learnerJVM/classtest/ClassTest;

2.2.15 attributes_count
u2:两个字节。
Offset0123456789 10 11 12 13 14 15 0000040000 01

class属性表集合的计数,这里有一个。
官方说明:
The value of the attributes_count item gives the number of attributes in the attributes table of this class.
解读:
接下里有一个文件的属性表集合
2.2.16 attributes[attributes_count]
attribute_info:属性表结构,长度attributes_count,从上文可知,一个。
结构如下:
attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length]; }

属性类型列表:
Attribute Location class file
SourceFile ClassFile 45.3
InnerClasses ClassFile 45.3
EnclosingMethod ClassFile 49.0
SourceDebugExtension ClassFile 49.0
BootstrapMethods ClassFile 51.0
Synthetic ClassFile, field_info, method_info 45.3
Deprecated ClassFile, field_info, method_info 45.3
Signature ClassFile, field_info, method_info 49.0
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations ClassFile, field_info, method_info 49.0
2.2.16.1 #1
Offset0123456789 10 11 12 13 14 15 0000040000 1000 00 00 02 00 11

结构 源码 解释
attribute_name_index 00 10 0x10,#16 = Utf8 SourceFile
attribute_length 00 00 00 02 两个字节
info[attribute_length] 00 11 由下图结构可知,0x11,#17 = Utf8 ClassTest.java
根据解读是SourceFile,结构如下:
SourceFile_attribute { u2 attribute_name_index; u4 attribute_length; u2 sourcefile_index; }

SourceFile属性用于记录生成这个 Class 文件的源码文件名称。
解读:
SourceFile: "ClassTest.java"

2.3 javap 工具结果 指令
javap -verbose -p ClassTest.class

结果
Classfile /D:/git/mybase/projects/github/learner-JVM/target/classes/com/hyl/learnerJVM/classtest/ClassTest.class Last modified 2020-8-7; size 414 bytes MD5 checksum cdd61b9867778e815b9d0b93b1412a9d Compiled from "ClassTest.java" public class com.hyl.learnerJVM.classtest.ClassTest minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref#4.#18// java/lang/Object."":()V #2 = Fieldref#3.#19// com/hyl/learnerJVM/classtest/ClassTest.n:J #3 = Class#20// com/hyl/learnerJVM/classtest/ClassTest #4 = Class#21// java/lang/Object #5 = Utf8n #6 = Utf8J #7 = Utf8 #8 = Utf8()V #9 = Utf8Code #10 = Utf8LineNumberTable #11 = Utf8LocalVariableTable #12 = Utf8this #13 = Utf8Lcom/hyl/learnerJVM/classtest/ClassTest; #14 = Utf8inLong #15 = Utf8()J #16 = Utf8SourceFile #17 = Utf8ClassTest.java #18 = NameAndType#7:#8// "":()V #19 = NameAndType#5:#6// n:J #20 = Utf8com/hyl/learnerJVM/classtest/ClassTest #21 = Utf8java/lang/Object { private long n; descriptor: J flags: ACC_PRIVATEpublic com.hyl.learnerJVM.classtest.ClassTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1// Method java/lang/Object."":()V 4: return LineNumberTable: line 9: 0 LocalVariableTable: StartLengthSlotNameSignature 050thisLcom/hyl/learnerJVM/classtest/ClassTest; public long inLong(); descriptor: ()J flags: ACC_PUBLIC Code: stack=4, locals=1, args_size=1 0: aload_0 1: getfield#2// Field n:J 4: lconst_1 5: ladd 6: lreturn LineNumberTable: line 14: 0 LocalVariableTable: StartLengthSlotNameSignature 070thisLcom/hyl/learnerJVM/classtest/ClassTest; } SourceFile: "ClassTest.java"

参考
  • 《深入理解Java虚拟机》第三版,周志明著。
  • Java虚拟机规范 https://docs.oracle.com/javase/specs/jvms/se14/html/index.html
  • Java字节码表 https://blog.csdn.net/u013490280/article/details/107853894

    推荐阅读