美文网首页
Class文件

Class文件

作者: 趁现在赶快回家 | 来源:发表于2017-05-12 21:52 被阅读0次

Java的跨平台特性建立在Java虚拟机之上。

  • Java虚拟机在不同平台上有不同的版本,但是他们都能执行同一class文件。
  • 任何编程语言的源代码,只要能编译成class文件,都能运行在Java虚拟机上面。例如Groovy语言。

1. Class文件结构

Class文件在Java虚拟机规范中的定义如下:

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

可以看出,其中依次包含魔数、小版本号、大版本号、常量池、类访问标志、当前类引用、超类引用、实现的接口引用、字段、方法以及类的属性。更详细、直观的结构如下图所示:

图1 Class文件总体结构<br>摘自《实战Java虚拟机:JVM故障诊断与性能优化》 ——葛一鸣

下面来逐一说明Class文件中的每一种结构。

1.1 常量池

常量池中有很多种常量,有一种可以通用来描述它们的结构:

cp_info {
    u1 tag;
    u1 info[];
}

每一种常量都以一个描述当前常量类型的u1(8字节无符号)整数tag开始,后面接上根据不同常量类型变化的2个或多个字节组成的,用来描述该常量携带的具体信息的东东。比如CONSTANT_Class_info常量

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

它的info[]就是u2(2字节无符号整数)表示的name_index。而CONSTANT_Integer_info的info[]则是u4(4字节无符号整数)表示的整数具体值:

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

截止到Java7,常量池包含了14种常量,他们各自的tag值如下表:

<h6 align = "center">表1 常量类型及其tag值</h6>

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
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

总的来说,常量池中的常量包括字面量和符号引用两类

  • 字面量。底层的数据类型,包括数字常量CONSTANT_Integer、CONSTANT_Float、CONSTANT_Long以及CONSTANT_Double和字符串常量CONSTANT_Utf8。它们的值就存储在各自的info[]之中。
  • 符号引用。包括类和接口名、字段和方法信息等。他们都通过索引直接或间接地指向CONSTANT_Utf8常量。

1.1.1 CONSTANT_Class

The CONSTANT_Class_info structure is used to represent a class or an interface:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

CONSTANT_Class类型的常量,通过name_index指向常量池中的一个CONSTANT_Utf8常量,用来表示自己所代表的类或接口名。

由于数组同样是对象,CONSTANT_Class表示数组的方式与方法与域的描述符中数组表示方式一样,如int[][]表示成[[I,Thread[]表示成[Ljava/lang/Thread。

1.1.2 CONSTANT_NameAndType

The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

1.1.3 CONSTANT_Fieldref, CONSTANT_Methodref, and CONSTANT_InterfaceMethodref

分别表示Fields, methods, and interface methods引用,结构相似:

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

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

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

由于CONSTANT_Class和CONSTANT_NameAndType可以完全确定一个字段或者方法,所以Fields, methods, and interface methods引用都含有:

  • class_index。指向CONSTANT_Class_info常量,代表该field或method所在的类,或者interface method所在的接口。
  • name_and_type_index。指向CONSTANT_NameAndType常量。

所以,CONSTANT_Fieldref, CONSTANT_Methodref, and CONSTANT_InterfaceMethodref与CONSTANT_Class和CONSTANT_NameAndType以及CONSTANT_Utf8的引用关系大致如下:

图2 常量引用关系<br>摘自 《自己动手写Java虚拟机》——张秀宏

常量池疑问

  1. 不太明白的是,为什么有了CONSTANT_Utf8还要CONSTANT_String,其实CONSTANT_String也是指向CONSTANT_Utf8的啊?
  2. CONSTANT_MethodHandle、CONSTANT_MethodType、CONSTANT_InvokeDynamic是Java7新加入的,它们用来干啥?我还没学到这儿来……

1.2 字段与方法

如图1所示,类的字段与方法具有相似的结构。以字段为例,字段包括字段数目以及字段具体信息数组:

u2             fields_count;
field_info     fields[fields_count];

如图1中看到的那样,字段具体信息的具体结构:

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
  • access_flags,字段访问标志
  • name_index,字段名称,再次无情指向常量池中的CONSTANT_Utf8。
  • descriptor_index,字段类型,同样指向常量池中的CONSTANT_Utf8。
  • attributes_count,属性数组的长度。一个字段可能拥有一些属性,用于存储额外信息,如初始化值、注释信息等。
  • attributes[attributes_count],属性数组。

字段疑问

  1. fields与常量池中的CONSTANT_Fieldref的关系?
  2. 看到field的属性中也许带有初始化值,突然想到《Java编程思想》中的一道题,定义2个String类型的字段,问在定义的时候初始化与在构造函数中初始化有什么不同?
    我当时编写了这样的程序:
public class Task2 {
        public String s1="has initialized";
        public String s2;
        public Task2(){
            s2="initialized in constructor";
        }
    }

编译之后,通过javap -c Task2 查看:

      Compiled from "Task2.java"
      public class peris.sky.learn.Task2 {
      public java.lang.String s1;

      public java.lang.String s2;

       public peris.sky.learn.Task2();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String has initialized
             7: putfield      #3                  // Field s1:Ljava/lang/String;
            10: aload_0
            11: ldc           #4                  // String initialized in constructor
            13: putfield      #5                  // Field s2:Ljava/lang/String;
            16: return
      }

发现,s1与s2的初始化其实都是在构造函数中完成的,只是先后顺序不一样。
学了class文件的字段之后,利用classpy查看class文件,发现s1与s2的属性表中,并没有初始化值,于是仔细阅读java虚拟机规范,发现这个初始化值只有常量(static and final)类型的field的属性表中才有。

1.3 属性

属性出现在class文件中的类属性表、字段属性表、方法属性表以及方法中的Code属性的属性表四种地方。属性与常量池中的常量类似,有多种类型,但都有相似的结构:

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

其中:

  • attribute_name_index,指向常量池中的CONSTANT_Utf8_info常量,表示该属性的类型(如表2所示)。
  • attribute_length,指出下一项info[]的长度。
  • info[attribute_length], 属性的具体信息,这个是跟随属性类型的不同而变化的。

<h6 align = "center">表2 属性类型、其可能出现的位置以及含义</h6>

AttributeType classfile field_info method_info Code 含义
ConstantValue y 常量字段的值
Code y 方法执行的字节码
StackMapTable y 类型检查的时候用
Exceptions y 方法可能抛出的一样信息
InnerClasses y 内部类
EnclosingMethod y
Synthetic y y y 源文件中不存在的类成员
Signature y y y
SourceFile y 源文件名
SourceDebugExtension y
LineNumberTable y 方法的行号
LocalVariableTable y 方法的局部变量信息
LocalVariableTypeTable y
Deprecated y y y 指出不建议使用的东东
RuntimeVisibleAnnotations y y y
RuntimeInvisibleAnnotations y y y
RuntimeVisibleParameterAnnotations y
RuntimeInvisibleParameterAnnotations y
AnnotationDefault y
BootstrapMethods y

1.3.1 Code属性

Code只存在于method_info之中,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];
}

可以看出,Code的info[]内容非常丰富:

  • max_stack,方法执行过程中,操作数栈不停地变化,在整个执行过程中存在着一个最大深度,这是在编译过程中决定的。
  • max_locals,局部变量表的最大size。相比于操作数栈,局部变量表像一个数组。
  • code[code_length],这个就是方法最重要的字节码咯,jvm把它翻译成一个又一个的jvm指令,然后就可以执行方法了。
  • exception_table,异常表,表中每一项是由start_pc、end_pc、handler_pc以及catch_type组成的结构体那样的东东。从方法字节码的start_pc偏移量开始到end_pc偏移量为止的这段代码中,如果遇到catch_type所指向的异常,那么代码就跳转到handler_pc的位置执行,说白了,就是写代码的try-catch结构。
  • attributes[attributes_count],Code属性的属性。从表2中可以看出Code属性可以拥有包括StackMapTable、LineNumberTable、LocalVariableTable以及LocalVariableTypeTable在内的4中属性。其中LineNumberTableLocalVariableTable以及LocalVariableTypeTable三个与类的SourceFile属性一起是调试时使用的。比如,LineNumberTable用来指定字节码与源文件中的行号的对应关系。 StackMapTable是用来class文件的类型检验。这4个属性都不是运行时必需的。

相关文章

  • ClassLoder学习笔记

    ClassLoder 负责加载class文件,class文件在文件开头有特定的文件标识,将class文件字节码内容...

  • Java类装载器

    一、负责加载class文件,class文件在文件开头有特定的文件标示,并且ClassLoader只负责class文...

  • java常用命令行参数

    运行class文件执行带main方法的class文件,命令行为:java 运行jar文件中的...

  • 热修复-从java文件到生成apk你需要知道的全过程

    一、从java文件到class文件 1.class文件 class文件就是一种文件格式,被JVM识别。如下图jav...

  • Discuz!数据库操作DB类和C::t类介绍

    类定义文件 DB类: 文件\source\class\class_core.php class DB extend...

  • 使用命令行执行Java程序

    确认运行环境 准备源文件生成文件: 编辑源文件内容 编译.class文件 运行.class文件

  • Android插件化技术——【class学习】

    class 文件的定义 class 文件就是能够被 JVM 识别,加载并且执行的文件格式。从定义来看,class ...

  • Class文件

    Class文件是啥 编译后被Java虚拟机所执行的代码使用了一种平台中立(不依赖于特定硬件及操作系统的)的二进制格...

  • .CLASS文件

    java class 文件是对Java程序二进制文件格式的精确定义。每一个Java class文件都对一个Java...

  • Class 文件

    总体结构 java 虚拟机规范的定义 u1 u2 u4 u8 分别表示 无符号单字节 2字节 4字节 8字节的整数...

网友评论

      本文标题:Class文件

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