zoukankan      html  css  js  c++  java
  • 深入理解JVM系列一:CLASS文件格式解析

    先写一段代码然后编译成class,直接对照16进制码阅读:

    1 package test;

    2 public class test {

    3         private int m;

    4

    5         public int inc() {

    6                 return m + 1;

    7         }

    8 }

    用winhex打开编译出的class后可以看到

    clip_image002

    CLASS文件的总体格式如下结构

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

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1>

    我们一点一点解读:

    1. 开头4字节是javaclass标志性的magicnumber,CAFEBABE

    2. 第5和6字节代表次版本号,一般都是0000

    3. 第7-8字节表示的是主板本号,值为0x34即52,表示编译器版本是jdk8

    4. 8-9字节是constant_pool入口,0x13即constant_pool_count表示了常量池中包含常量的数目是18=constant_pool_count-1

    5. 接下来是每个常量池项的具体信息,常量池主要存放两大类常量:Literal 和 Symbolic Reference,Literal表示一些字符串常量以及声明为final的常量等,Symblic Reference则表示类和接口名、字段名、方法名等。

    偏移为0xA位置的值为0xA,通过查表(1)可得这是一个CONSTANT_Methodref(字段),字段结构如下:

    CONSTANT_Methodref_info {

               u1 tag;

               u2 class_index;

               u2 name_and_type_index;

    }

    tag之后是class_index值为0x0004,class_index 项的值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Class_info结构,表示一个类或接口,当前字段或方法是这个类或接口的成员,值得注意的是如果一个 CONSTANT_Methodref_info 结构的方法名以“<”('\u003c')开头,说明这个方法名是特殊的,即这个方法是实例初始化方法。接着就是name_and_type_index,值为0x000f,name_and_type_index 项的值必须是对常量池的有效索引,常量池在该索引处的项必须是 CONSTANT_NameAndType_info结构,它表示当前字段或方法的名字和描述符。

    偏移为0xF位置的值为0x9,是一个CONSTANT_Fieldref和CONSTANT_Methodref有一样的结构,详细说明参见:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1

    偏移为0x14位置的值为0x7是一个CONSTANT_Class,我们跳过,偏移为0x17位置的值为0x7也是一个CONSTANT_Class,并且他就是最开始CONSTANT_Methodref第二个字段所指向的。

    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

    表(1)

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1>

    当然大可不必这么麻烦,javap可以轻松输出这些分析javap -verbose test.class:

    clip_image004

    从这里可以看出java所有类会继承java.lang.object,编译器会为没有显示构造函数的类做出一个默认的构造函数出来,在创建对象时,先调用父类默认构造函数对对象进行初始化。()V应该就是void的意思,I即int。

    6. 接下来是access_flags位于0x9B,可以看到constant_pool占了很大一部分空间,access_flags值为0x0021,从表(2)看出这是一个public类,然后还有ACC_SUPER属性,在 JDK 1.0.2 之后编译出的 Class 文件,都带有 ACC_SUPER 标志用以和以前版本区分。这里说明一下invokespecial指令,每个类都至少会有一个<init>()方法,这些方法通常是用invokespecial调用。

    Flag Name

    Value

    Interpretation

    ACC_PUBLIC

    0x0001

    Declared public; may be accessed from outside its package.

    ACC_FINAL

    0x0010

    Declared final; no subclasses allowed.

    ACC_SUPER

    0x0020

    Treat superclass methods specially when invoked by the invokespecial instruction.

    ACC_INTERFACE

    0x0200

    Is an interface, not a class.

    ACC_ABSTRACT

    0x0400

    Declared abstract; must not be instantiated.

    ACC_SYNTHETIC

    0x1000

    Declared synthetic; not present in the source code.

    ACC_ANNOTATION

    0x2000

    Declared as an annotation type.

    ACC_ENUM

    0x4000

    Declared as an enum type.

    表(2Class access and property modifiers

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1>

    7. 然后是this_class,值为0x0003是对constant_pool表中项目的一个有效索引值,从字面意思就是当前类了。

    8. 之后是super_class,值为0x0004,即java.lang.object,如果 Class 文件的 super_class 的值为 0,那这个 Class 文件只可能是定义的是 java.lang.Object 类,只有它是唯一没有父类的类。

    9. Interface_count表示当前类或接口的直接父接口数量,值为0即没有。

    10. interfaces[]字段直接就没有了,可以看出class文件的紧凑。

    11. 位于0xA2处的0x0001是field_count,表示当前 Class 文件 fields[]数组的成员个数。

    12. 之后就是fieldtable的一个项了,filedtable项的结构为:

    field_info {
          u2 access_flags;
          u2 name_index;
          u2 descriptor_index;
          u2 attributes_count;
    attribute_info attributes[attributes_count];
    }来自 <
    https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1>

       Fileld_info值依次为0x0002 0x0005 0x0006 分别表示 private m int,随后的attributes_count为0,attribute项用来表示标识符的修饰,例如如果定义private static int m;则这里就会有constantvalue项。

    13. 紧随其后的0xAC位置表示method_count,值为0x0002,接下来的0x0001 0x0007 0x0008分别表示public <init> void,这个初始化函数有attribute,attribute结构为:

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

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7>

    attribute_name_index值为0x0009,在constant_pool中索引为code,此属性为code描述符即java代码编译器编译成的字节码指令,之后的length为0x001D。

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

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3>

    随后的max_stack和max_locals都为1,max_stack代表了操作数栈深度的最大值,max_local代表了局部变量表所需空间,单位为slot,长度不超过32位的数据类型占一个slot,64位的数据如double和long需要2个slot。java编译器会根据变量的作用域来分配slot给各个变量使用。

    Code_length为5,紧接着的便是java指令字节码,0x2AB7000AB1对应指令为,即加载参数、初始化、返回。等等哪里来的参数呢?实际上就是this啦。

    clip_image006

    接下来是异常处理,也是有点复杂,就跳过了,直接进入下一个方法入口位于0xD9处,分析同上。

    直到0x106,两个方法结束。

    14. 接着一个属性从0x106开始,name_index=0xD,为sourcefile,其格式为:

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

    来自 <https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3>

    对应的值为test.java即文件名。

  • 相关阅读:
    蚂蚁金服SOFAMesh在多语言上的实践
    2018第48周日
    git只拉取github部分代码的方法
    深入理解brew link命令
    openssl/ssl.h file not found
    react热加载失败
    pycharm批量清楚pyc、$py.class文件
    Hash history cannot PUSH the same path; a new entry will not be added to the history stack
    JavaScript indexOf() 方法,获取元素的位置;Object.keys()获取对象的所有key的数组
    JavaScript删除数组里的某个元素
  • 原文地址:https://www.cnblogs.com/liujshi/p/4583776.html
Copyright © 2011-2022 走看看