9.Class类文件内部结构

Class文件结构

在加类加载子系统之前,我们需要首先了解Class文件的内部结构。 
Class文件是一种平台无关的二进制字节码格式(ByteCode),该字节码能够被JVM识别并解释执行或被JVM进一步编译成本地机器码(Native Code)后执行。Class文件以8位字节为单位,排列紧凑中间没有任何分隔符。 
Class文件结构。

ClassFile {

    //魔数固定位0xcafebabe

    u4              magic;

    //Class文件版本号和JDK版本

    u2              minor_version;

    u2              major_version;

    //常量池

    u2              constant_pool_count;

    cp_info         contant_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];

}

常量池

常量池主要存放字面量(Literal)符号引用(Symbolic Reference)。 
字面量如:文本字符串(如:String “abc”)、声明为final的常量值。 
符号引用如:

·        类和接口的权限定名(如: “java.lang.Object”

·        字段的名称和描述符

·        方法的名称和描述符

Java编译和C/C++不同,没有链接Linking步骤,编译后的Class文件中只是方法和字段的符号引用,而没有真正内存地址。当虚拟机运行时需要从常量池获取对应符号引用,在类加载的才将符号引用翻译成具体地址;甚至在方法多态情况下在每次方法执行时才翻译为具体地址。

常量池项目类型主要有

CONSTANT_Utf8_info     方法、字段等都需要用该类型常量描述 
CONSTANT_String_info    
字符串类型字面量 
CONSTANT_FieldRef_info    
字段符号引用 
CONSTANT_MethodRef_info    
类中方法符号引用 
CONSTANT_Class_info    
类或接口引用符号(类全限定名)

例子如下: 

package org.jvminternals;

publicclass SimpleClass {

    publicvoidsayHello() {

        System.out.println("Hello");

    }

 

}

对应常量池:

public class org.jvminternals.SimpleClass

  SourceFile: "SimpleClass.java"

  minor version: 0

  major version: 51

  flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

   #1= Methodref          #6.#17         // java/lang/Object."<init>":()V

   #2= Fieldref           #18.#19        // java/lang/System.out:Ljava/io/PrintStream;

   #3=String             #20            //  "Hello"

   #4= Methodref          #21.#22        // java/io/PrintStream.println:(Ljava/lang/String;)V

   #5= Class              #23           //  org/jvminternals/SimpleClass

   #6= Class              #24            //  java/lang/Object

   #7= Utf8               <init>

   #8= Utf8               ()V

   #9= Utf8               Code

  #10= Utf8               LineNumberTable

  #11= Utf8              LocalVariableTable

  #12= Utf8               this

  #13= Utf8              Lorg/jvminternals/SimpleClass;

  #14= Utf8               sayHello

  #15= Utf8               SourceFile

  #16= Utf8               SimpleClass.java

  #17= NameAndType        #7:#8          //  "<init>":()V

  #18= Class              #25            //  java/lang/System

  #19= NameAndType        #26:#27       // out:Ljava/io/PrintStream;

  #20= Utf8               Hello

  #21= Class              #28            //  java/io/PrintStream

  #22= NameAndType        #29:#30       // println:(Ljava/lang/String;)V

  #23= Utf8              org/jvminternals/SimpleClass

  #24= Utf8               java/lang/Object

  #25= Utf8               java/lang/System

  #26= Utf8               out

  #27= Utf8              Ljava/io/PrintStream;

  #28= Utf8              java/io/PrintStream

  #29= Utf8               println

  #30= Utf8               (Ljava/lang/String;)V

访问标志

标示访问信息:是类还是接口?是否为public类型?是否定义abstract? 

类索引、父类索引、实现的接口索引 
this_class
: 在例子中是常量池偏移5,最终指向本类权限定名常量:org/jvminternals/SimpleClass 
supr_class:父类,同理。本例:java/lang/Object 
interfaces[interfaces_count]:接口同理,只不过Java支持实现多接口。 

字段表集合:类变量或者实例变量。

方法表集合 

9.Class类文件内部结构

属性表集合 
属性表集合中最重要的部分就是Code属性,用于java代码编译后的字节码指令。 
Code属性如下:

9.Class类文件内部结构

上面例子,编译后的字节码:

{

  public org.jvminternals.SimpleClass();

    Signature: ()V

    flags: ACC_PUBLIC

    Code:

      stack=1, locals=1, args_size=1

        0: aload_0

        1: invokespecial #1   // Methodjava/lang/Object."<init>":()V

        4: return

 

  publicvoid sayHello();

    Signature: ()V

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=1, args_size=1

        0: getstatic      #2    // Field java/lang/System.out:Ljava/io/PrintStream;

        3: ldc            #3   // String "Hello"

        5: invokevirtual  #4    // Method java/io/PrintStream.println:(Ljava/lang/String;)V

        8: return

 }