美文网首页
Java Class文件

Java Class文件

作者: wangdy12 | 来源:发表于2017-10-31 20:49 被阅读0次

类文件(.class文件扩展名)是包含Java字节码 ByteCode的文件,可以在Java虚拟机上执行,每个类文件包含了一个类,接口或者模块(Java 9)的定义

Java程序(.java 文件)可以通过 Java compiler 生成字节码文件,其他基于JVM的语言也都可以通过自己的编译器生成字节码文件,例如Scala,Groovy等

JVM是与平台无关的,类文件可以在多个平台上执行,这使得相应的语言也与平台无关。

Class类文件的结构

类文件由8位字节流组成, 多字节数据使用big-endian的方式存储
类文件是按照ClassFile结构生成的字节流,字段连续存储,没有任何填充或对齐。
Class文件格式采是用类C的结构符号表示的伪结构存储数据,有两种数据类型,无符号数(u1,u2,u4,u8)和表(*_info)

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

Class 文件中涉及类/方法等名称和描述

名称

Binary Class and Interface Names
全限定名称,也叫二进制名称
例如 Thread正常的二进制名称为 java.lang.Thread ,但是因为一些历史原因,内部格式下,.被替换成了正斜线/,使用CONSTANT_Utf8_info结构(即Utf8编码的字符串常量)表示其为java/lang/Thread

Unqualified Names
非限定名称,即简单名称,例如Thread

描述符

描述符是表示字段或方法类型(type)的字符串,使用modified UTF-8(UTF-8缩略编码)

Field Descriptors
基本数据类型用一个大写字符表示,int对应 I
对象类型使用一个L加上全限定,末尾加上分号;表示,Object对应Ljava/lang/Object;
数组每个维度使用一个前置[表示,double[][][]对应[[[D

Interpretation of field descriptors

Method Descriptors
由零个或者多个参数描述符,加上一个返回值描述符组成
( {ParameterDescriptor} ) ReturnDescriptor

Object m(int i, double d, Thread t) {...}
对应
(IDLjava/lang/Thread;)Ljava/lang/Object;


magic & version

魔数magic number :识别类文件格式,值为0xCAFEBABE
版本号:M.m(主版本号M,副版本号 m)
major version从45开始的,每个大版本号向上加1
JDk1.1.* 支持45.0 到 45.65535
JDk1.8.* 支持45.0 到 52.65535
JDk1.k.* 支持主版本号45.0 到 44+k.0 (k>=2)

常量池

常量池数目constant_pool_count,数目等于常量表的条目数+1
常量池constant_pool是一张表,结构化的表示各种字符串常量,类和接口名称,字段名称等,表索引为1到constant_pool_count - 1

它的用处在于:Java虚拟机指令不依赖于运行时的布局,编译时没有链接这一步,所以指令引用的是constant_pool表中的符号信息

通用结构

cp_info {
    u1 tag;
    u1 info[];
}

表中的每一项都以一个1byte的标志位开头,指明该项的常量类型,之后是紧跟相应的内容,每种类型都有自己的结构

Constant pool tags

CONSTANT_Class_info 结构

CONSTANT_Class_info {
      u1 tag;
      u2 name_index;
}

tag :CONSTANT_Class (7)
name_index:是constant_pool中有效的索引,其对应的位置必定是一个CONSTANT_Utf8_info结构,表示类或接口的名称

CONSTANT_Utf8_info 结构

CONSTANT_Utf8_info {
      u1 tag;
      u2 length;
      u1 bytes[length];
}

tag :CONSTANT_Utf8 (1)
length:长度,限定了最大长度为65535
bytes[]:Modified UTF-8形式的字符串

CONSTANT_Fieldref_info 结构
用于类加载过程中的解析

CONSTANT_Fieldref_info {
      u1 tag;
      u2 class_index;
      u2 name_and_type_index;
}

CONSTANT_Fieldref_info, CONSTANT_Methodref_info, CONSTANT_InterfaceMethodref_info三种常量结构相似, tag作为标识,class_index指向相应的类或接口类型信息的常量,name_and_type_index指向CONSTANT_NameAndType_info结构,表示方法或字段的名称和描述符

访问标志 access_flags

类或接口的访问权限和属性的标志掩码

Class access and property modifiers

自身和父类的信息 this_class / super_class

u2类型,指向CONSTANT_Class_info的索引,父类的索引可以为0(java.lang.Object没有父类),可以获取对应的类名称

接口信息 interfaces_count / interfaces[]

描述了该类扩展的接口信息,interfaces[i] 都是constant_pool表中的CONSTANT_Class_info的索引, 0 ≤ i < interfaces_count,接口的顺序按照源文件中的实现从左向右排列

字段表

fields_countfields表中field_info结构的数目
field_info结构代表了类或接口中所有的字段(类变量class variables(静态变量),实例变量instance variables(类实例中包含的变量)),但是不包含他们的父类或者父接口继承来的字段

field_info {
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
Field access and property flags

name_index:指向CONSTANT_Utf8_info结构的索引,表示一个有效的非限定名称
descriptor_index:指向CONSTANT_Utf8_info结构的索引,表示字段的描述符

之后是属性信息,存储一些额外的信息,例如对于final static int m = 1,有ConstantValue属性,包含静态变量的赋值信息。

方法表

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

同字段表类似,访问标识有部分不同


Method access and property flags

方法中的Java代码,经过编译器,编译成字节码格式之后,存放在方法的属性表内的Code属性内
方法表中可能会有编译器自己添加的方法,例如<init><clinit>

属性表

ClassFile, field_info, method_info, and Code_attribute 结构中,都含有属性
Java 9共有26种预定义的属性

Predefined class file attributes

每个属性的名称用CONSTANT_Utf8_info表示,属性的结构如下

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

Code属性

是可变长度的属性,包含了一个方法的JVM指令和辅助信息
nativeabstract类没有Code属性

//Code 属性的结构
Code_attribute {
    u2 attribute_name_index; //对应一个`CONSTANT_Utf8_info `索引,内容是Code
    u4 attribute_length;
    u2 max_stack; //operand stack 操作数栈的最大深度
    u2 max_locals;//局部变量数组的长度
    u4 code_length; //长度小于65536
    u1 code[code_length];//字节码指令
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc; //在code数组中的起点和终点[start_pa, end_pc)
        u2 handler_pc; //异常处理对应的字节码起始位置
        u2 catch_type;// 是常量池中 CONSTANT_Class_info的索引,表示捕获的异常类型,为0则处理任何异常 
    } exception_table[exception_table_length];//异常表
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

Exception属性

声明所有的受查异常(checked exception,不是派生于ErrorRuntimeException的其他所有异常)

Exceptions_attribute {
    u2 attribute_name_index; //索引指向`CONSTANT_Utf8_info `表示"Exceptions"
    u4 attribute_length;
    u2 number_of_exceptions;
    u2 exception_index_table[number_of_exceptions]; //索引指向`CONSTANT_Class_info`,表示受查异常的类型
}

LineNumberTable

出现在Code属性的attributes表中,显示code字节码和相应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];
}

LocalVariableTypeTable

局部变量表,出现在Code属性的attributes表中,用来在调试过程中描述局部变量的具体类型

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc; //对应局部变量的开始位置
        u2 length;//作用域[start_pc, start_pc + length)
        u2 name_index;//名称
        u2 signature_index;//签名信息,和描述符类似,保留了泛型类型
        u2 index;//局部变量在当前栈帧局部变量表中的位置索引
    } local_variable_table[local_variable_table_length];
}
函数示例

SourceFile

记录源码文件的名称

Signature

Signature_attribute {
    u2 attribute_name_index; // "Signature"
    u4 attribute_length;  //必定是2
    u2 signature_index; //对应CONSTANT_Utf8_info,表示类,接口,构造器,方法,字段的签名
}

Java字节码的方法的特征签名,包含返回值和受查异常表(详细信息,参考jvms9 4.7.9.1)
[TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}

InnerClass

记录内部类和宿主类之间的关联

InnerClasses_attribute {
    u2 attribute_name_index; 
    u4 attribute_length;
    u2 number_of_classes;
    {   u2 inner_class_info_index;//内部类或接口信息CONSTANT_Class_info
        u2 outer_class_info_index;//外部类或接口信息CONSTANT_Class_info
        u2 inner_name_index;//内部类的名称CONSTANT_Class_info,匿名内部类名称为0
        u2 inner_class_access_flags;
    } classes[number_of_classes];
}
Nested class access and property flags

ConstantValue

静态变量相关的一个定长属性

ConstantValue_attribute {
      u2 attribute_name_index;
      u4 attribute_length;
      u2 constantvalue_index;
}
常量池中对应字段的类型

javap

反编译Class文件,没有选项时输出包,类里的protected和public字段和方法

用法:javap [options] classfile

  • -verbose 打印细节,可以查看常量表,栈大小,各方法的 locals 及 args 数等
  • -p -private 显示所有的类和成员
  • -c 输出类中各方法的 Java 字节码的指令
  • -s signatures,输出内部类型签名

Reference:
Java Language and Virtual Machine Specifications
The Java® Virtual Machine Specification Java SE 9 Edition

相关文章

网友评论

      本文标题:Java Class文件

      本文链接:https://www.haomeiwen.com/subject/yrdkpxtx.html