本文主要基于Java SE 8 版本的Java虚拟机规范。

Java Class File Format

Java虚拟机规范

https://docs.oracle.com/javase/specs/index.html

Java SE 8 虚拟机规范

https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf

工欲善其事,必先利其器。

参考书 《Java虚拟机规范(Java SE 8版)》

https://book.douban.com/subject/26418340/

IDEA插件 - BinEd

Binary/hexadecimal editor plugin based on BinEd library.

  • Use “Open as Binary” action in main “File” menu or in project files context menu.
  • Use “View as Binary” action in context menu in variables/debug window.
  • Associate file extension with Binary File file type in Options/Editor/File Types

基于BinEd库的二进制/十六进制编辑插件。

  • 在"File"主菜单或项目文件上下文菜单下使用 "Open as Binary"行动.
  • 在上下文菜单中使用"View as Binary"行动在变量/调试窗口
  • 将文件扩展名与二进制文件文件类型选项/编辑/文件类型

IDEA插件 - JClassLib

jclasslib is a bytecode viewer for Java class files。

jclasslib是一个查看Java类文件字节码的浏览器。

Class File Format

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     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

Magic Number

Magic Number长度为4字节,通过固定值:0xCAFEBABE来表示这是一个java class文件。

Minor Version、Major Version

Minor Version和Major Version都是2字节,用来表示class文件的副、主版本。

Java8的Major Version是0x0034(十进制为52),Java7的Major Version是0x0033(十进制为51)。

Constant Pool Count、Constant Pool

Constant Pool Count和Constant Pool组成了常量池,Constant Pool Count用来表示常量池中常量的数量,Constant Pool是长度为Constant Pool Count减一的数组。

** 常量池的编号为从1开始的,默认存在一个表示空指向的编号0。

常量池中的所有项通用格式

cp_info {
    u1 tag;
    u1 info[]; 
}

todo 规范上tag项有14种,具体懒得写了,有空补上。

Access Flags

Access Flags的长度为2字节,是一种由标志所构成的掩码,用来表示类的访问权限以及其他属性。

Flag Name 1 Value Interpretation 含义
ACC_PUBLIC 0x0001 声明 public; 可以从包外访问。
ACC_FINAL 0x0010 声明 final; 不允许有子类。
ACC_SUPER 0x0020 当用到invokespecial指令时,需要对父类方法做特殊处理。JDK1.0.2之后编译出来的必须为真。
ACC_INTERFACE 0x0200 是接口,而不是一个类。
ACC_ABSTRACT 0x0400 声明 abstract; 不能被实例化。
ACC_SYNTHETIC 0x1000 声明 synthetic; 编译器自动生成,非用户代码生成。
ACC_ANNOTATION 0x2000 声明annotation类型。
ACC_ENUM 0x4000 声明enum类型。

this_class、super_class

this_class和super_class长度都是2个字节,里面存了一个常量表索引,通过这个索引可以找到一个CONSTANT_Class_info常量。两者不同的是this_class表示类名,而super_class表示父类类名。

interfaces_count、interfaces

和this_class差不多,因为接口可以多实现,所以有interfaces_count计数。interfaces是一组常量表索引,通过索引可以找到对应的CONSTANT_Class_info常量。

fields_count、fields

fields_count的值表示当前 Class 文件 fields[]数组的成员个数。 fields[]数组中每一项都是一个field_info结构的数据项,它用于表示该类或接口声明的类字段或者实例字段。

fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接口继承的部分。

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

methods_count、methods

methods_count的值表示当前Class 文件 methods[]数组的成员个数。Methods[]数组中每一项都是一个 method_info 结构的数据项,用于表示当前类或接口中某个方法的完整描述。

如果某个method_info 结构的access_flags 项既没有设置 ACC_NATIVE 标志也没有设置ACC_ABSTRACT 标志,那么它所对应的方法体就应当可以被 Java 虚拟机直接从当前类加载,而不需要引用其它类。

method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法方法和类或接口初始化方法方法 。

methods[]数组只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

只要不是NATIVE或ABSTRACT方法,那这个函数的attributes里面肯定有一个Code,Code的attributes里面又有LineNumberTable_attribute(行数表)和LocalVariableTable_attribute(本地变量表)。

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];
}

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];
}

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];
}

attributes_count、attributes

attributes_count的值表示当前 Class 文件attributes表的成员个数。attributes表中每一项都是一个attribute_info结构。

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

attribute_info只是一个通用模型,比如SourceFile_attribute实际长这样。

SourceFile_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 sourcefile_index;
}