概述
- 字节码采用Big-Endian,是Spark、PowerPC等处理器的默认字节序;x86则是用Little-Endian
- 字节码文件采用一种类似于C语言结构体的伪结构来存储数据,只有两种数据类型:无符号数和表
- 无符号数
- 基本数据类型,以u1,u2,u4,u8分别代表1,2,4,8字节的无符号数
- 可用来描述数字、索引引用、数量值或者按UTF-8编码构成字符串值
- 表
- 由多个无符号数或者其他表作为数据项构成的复合数据类型
- 习惯性地以“_info”结尾
- 整个Class文件本质上也就是一张表
文件结构
ClassFile {
u4 magic; # 0xCAFEBABE
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info contant_pool[constant_pool_count – ]; # 之所以-1,是因为#0被做他用了
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
magic, minor_version, major_version
specifies information about the version of the class, and the version of the JDK this class was compiled for
constant_pool
similar to a symbol table although it contains more data this is described in more detail below
access_flags
provides the list of modifiers for this class
this_class
index into the constant_pool providing the fully qualified name of this class i.e. org/jamesdbloom/foo/Bar
super_class
index into the constant_pool providing a symbolic reference to the super class i.e. java/lang/Object
interfaces
array of indexes into the constant_pool providing a symbolic references to all interfaces that have been implemented
fields
array of indexes into the constant_pool giving a complete description of each field
methods
array of indexes into the constant_pool giving a complete description of each method signature, if the method is not abstract or native then the bytecode is also present
attributes
array of different value that provide additional information about the class including any annotations with RetentionPolicy.CLASS or RetentionPolicy.RUNTIME
结构详解
- 魔数
- 标识class文件
- 大小版本
- major_version.minor_version合在一起为class文件版本,编译器产生,如59.0,与JDK版本相关
- 常量池
- 相同类型值在常量池中归一,以索引供外部引用,缩减字节码文件大小,便于网络传输的初衷
- 逻辑类型:字面量(literal) + 符号引用(symbolic reference)
- 常量池中装常量,每个常量都是一个表,一共14种常量(JDk 7之前只有前11种)
访问标识
名称 | 值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_FINAL | 0x0010 | final,不可被继承 |
ACC_SUPER | 0x0020 | 兼容早期编译器,新编译器均设该标记,invokespecial指令会对子类方法做特定处理 |
ACC_INTERFACE | 0x0200 | 接口,需同时设置ABSTRACT |
ACC_ABSTRACT | 0x0400 | 抽象类 |
ACC_SYNTHETIC | 0x1000 | synthetic,编译器产生 |
ACC_ANNOTATION | 0x2000 | 注解,需同时INTERFACE和ABSTRACT |
ACC_ENUM | 0x4000 | 枚举 |
- 多项值:或运算
- PUBLIC+SUPER 0x0021
字段表
- 类变量,实例变量,但不含继承变量,不含局部变量
- Z(boolean) B C S I J(long) F D V L(object)
名称 | 类型 | 描述 |
---|---|---|
access_flags | u2 | 见属性访问标识 |
name_index | u2 | 名称索引 |
descriptor_index | u2 | 描述符索引 |
attributes_count | u2 | 属性个数 |
attributes[attributes_count] | attribute_info | 属性集合 |
- 属性访问标识
名称 | 值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_PRIVATE | 0x0002 | private |
ACC_PROTECTED | 0x0004 | protected |
ACC_STATIC | 0x0008 | static |
ACC_FIANL | 0x0010 | final |
ACC_VOLATILE | 0x0040 | volatile,直接读写内存,不可被缓存;与FINAL互斥 |
ACC_TRANSIENT | 0x0080 | transient |
ACC_SYNTHETIC | 0x1000 | synthetic |
ACC_ENUM | 0x4000 | enum |
方法表
名称 | 类型 | 描述 |
---|---|---|
access_flags | u2 | 见方法访问标识 |
name_index | u2 | 名称索引 |
descriptor_index | u2 | 描述符索引 |
attributes_count | u2 | 属性个数 |
attributes[attributes_count] | attribute_info | 属性集合 |
- 方法访问标识
名称 | 值 | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_PRIVATE | 0x0002 | private |
ACC_PROTECTED | 0x0004 | protected |
ACC_STATIC | 0x0008 | static |
ACC_FIANL | 0x0010 | final |
ACC_SYNCHRONIZED | 0x0040 | synchronized |
ACC_BRIDGE | 0x0080 | 桥接方法,泛型时出现过 |
ACC_VARARGS | 0x0080 | 含不定参数 |
ACC_NATIVE | 0x0080 | native |
ACC_ABSTRACT | 0x0080 | abstract |
ACC_STRICTFP | 0x1000 | strictfp |
ACC_SYNTHETIC | 0x4000 | synthetic,编译器产生,如, |
字节码命令
# 变量操作
aload_n # 本地变量数组->操作数栈 a代表对象引用,n代表本地变量数组索引
iload # int
lload # long
fload # float
dload # double
ldc # 运行时常量池常量->操作数栈
getstatic # 运行时常量池静态变量->操作数栈
# 方法
invokedynamic
invokeinterface
invokespecial # 实例初始化,私有方法,父类方法
invokestatic
invokevirtual # 普通实例方法
# return语句
return:void
ireturn:int
lreturn:long
freturn:float
dreturn:double
areturn:object reference
# java源码
Object foo = new Object();
# 字节码
: new #2 // Class java/lang/Object
: dup
: invokespecial #3 // Method java/lang/Object:"<init>"( ) V
# 堆中开了块内存,返回对象引用至操作数栈
# 复制栈顶对象引用,塞入栈顶
# 对象初始化,消耗栈顶对象引用(作为参数)
# 留下的那个栈顶对象引用,就是初始化好的对象引用
字节码文件例子
package some;
public class SimpleClass {
private int m;
public int inc() {
return m + ;
}
}
cafe babe
d65 53 d c c61
a61 6c e
62 a65 d
c e
a00 b0c
4c e e d c65
c 63 c c65
c c
d65 53 d c c61
b01 e
c00 a
c65 d c c61
a
00
a b700 ab1 c
d c
e
ab4
ac00 c00
d00 c00
00
javap -v -p -s -sysinfo -constant some/SimpleClass.class
package some;
public class SimpleClass {
public int a = ;//这个1并不会出现在常量池中,而a以Filedref出现
final int b = ;//这个3会以Integer表的身份出现在常量池中,b也以Fieldref出现
public void sayHello() {
final int c = ;//只有c以Utf8出现,10并没有出现(这个final待究)
System.out.println(c);
System.out.println("Hello");
}
}