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即文件名。

  • 相关阅读:
    centos7.6 使用yum安装mysql5.7
    解决hadoop本地库问题
    docker-compose 启动警告
    docker 安装zabbix5.0 界面乱码问题解决
    docker 部署zabbix问题
    zookeeper 超时问题
    hbase regionserver异常宕机
    (转载)hadoop 滚动升级
    hadoop Requested data length 86483783 is longer than maximum configured RPC length
    zkfc 异常退出问题,报错Received stat error from Zookeeper. code:CONNECTIONLOSS
  • 原文地址:https://www.cnblogs.com/liujshi/p/4583776.html
Copyright © 2011-2022 走看看