zoukankan      html  css  js  c++  java
  • JVM Class详解之一

    首先看Class中包含哪些信息简单的说所有Java文件中有的信息class文件都有,编译器帮我们将java文件转化成了JVM能看懂的class格式而已

    Class 概述

    Class文件是一组以8位字节为基础的二进制流,各个数据项目按照严格顺序紧凑排列在Class文件中。
    所有的16位,32位,64位长度的数据将被构造成2个,4个,8个字节单位来标示。

    ClassFile结构

    类型名称数量
    u4 magic 1
    u2 minor_version 1
    u2 major_version 1
    u2 constant_pool_count 1
    cp_info constant_pool constant_pool_count-1
    u2 access_flags 1
    u2 this_class 1
    u2 super_class 1
    u2 interfaces_count 1
    u2 interfaces interfaces_count
    u2 fields_count 1
    field_info fields fields_count
    u2 methods_count 1
    method_info methods methods_count
    u2 attributes_count 1
    attribute_info attributes attributes_count

    class格式说明

    • magic:魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE
    • minor_version、major_version: 分别为Class文件的副版本和主版本
    • constant_pool_count: 常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1
    • constant_pool[]: 常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池不同于其他,索引从1开始到constant_pool_count -1
    • access_flags: 访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性
    • this_class: 类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值
    • super_class: 父类索引
    • interfaces_count: 接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量
    • interfaces[]: 接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count
    • fields_count: 字段计数器
    • fields[]: 字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项
    • methods_count: 方法计数器
    • methods[]: 方法表,methods[]数组中的每个成员都必须是一个method_info结构的数据项
    • attributes_count: 属性计数器
    • attributes[]: 属性表,attributes表的每个项的值必须是attribute_info结构

    废话不多说HelloWorld搞起

    public class HelloWorld
    {
    String str = "";
    
    public String getStr()
    {
    return str;
    }
    
    public void setStr(String str)
    {
    this.str = str;
    }
    }
    

    编译成class文件以后,只用javap -verbose HelloWorld.class 指令可以查看当前class的内容
    screenshot
    同时使用UE打开class文件
    screenshot
    我们来一起看下ClassFile结构
    screenshot
    前4个字节为魔数,也就是0xCAFEBABE,这里都是十六进制
    screenshot
    魔数后2个字节为副版本号,这里副版本号是0
    screenshot
    主版本号0x0033,转为十进制,主版本号是51 标示当前class是通过jdk 1.7编译的,0x32是jdk1.6 0x31是jdk1.5
    screenshot
    这两个字节是常量池计数器,常量池的数量为0x001A,转为十进制是26,也就是说常量池索引为1~26
    screenshot

    从常量池开始

    常量池计数器后面紧跟着就是常量池的内容
    所有的常量池项都具有如下通用格式:

    cp_info 
    { 
    u1 tag; 
    u1 info[]; 
    }
    CONSTANT_Class_info 
    { 
    u1 tag; 
    u2 name_index; 
    }
    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_NameAndType_info 
    { 
    u1 tag; 
    u2 name_index; 
    u2 descriptor_index; 
    }
    CONSTANT_Utf8_info 
    { 
    u1 tag; 
    u2 length; 
    u1 bytes[length]; 
    }
    CONSTANT_MethodHandle_info 
    {
    u1 tag; 
    u1 reference_kind; 
    u2 reference_index; 
    }
    

    screenshot

    screenshot
    后面的0x07对应tag找到是CONSTANT_Class,标示接下来的是一个class的信息。后面的 00 02 是class的name_index 标示指向常量池的第二个常量。我们再看第二个常量
    screenshot
    第二个常量是01开头,我们查看常量类型表中对应是Utf-8,再按照utf-8的结构,后面的00 0A代表了这个utf-8的长度这里长度转换为10进制是11,后面紧跟着utf-8的实际内容
    screenshot
    screenshot
    再后面0x 07,是常量池的下一个产量,也是一个class信息,后面跟00 04,name_index执行常量池的
    第4个常量。
    screenshot
    第4个常量又是utf-8,后面长度为 0x10 十进制为16,接下来的为实际内容
    screenshot
    接下来都可以按照此方法分析。
    直观结果可以通过javap指令查看
    screenshot

    screenshot
    常量池后面紧跟的2个字节是Access Flag,这个表示用于标示类或接口层次的访问信息,如这个Class是类还是接口,是否为public类型,是否定义为abstrace类型。
    标志名称 标志值 含义
    ACC_PUBLIC 0x0001 是否为public类型
    ACC_FINAL 0x0010 是否被声明为final,只有类可设置
    ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令,JDK1.2以后编译出来的类这个标志为真
    ACC_INTERFACE 0x0200 标识这是一个接口
    ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口和抽象类,此标志为真,其它类为假
    ACC_SYNTHETIC 0x1000 标识别这个类并非由用户代码产生
    ACC_ANNOTATION 0x2000 标识这是一个注解
    ACC_ENUM 0x4000 标识这是一个枚举
    我们这里0021标示为public Class

    接下来的是类索引,父类索引与接口索引集合
    this_class,super_class,interfaces_count,interfaces

    类索引

    为2个字节
    screenshot
    这里为00 01,指向常量池中第一个常量,之前我们分析过常量池中第一个常量为Class类型,内容指向第二个常量UTF-8的HelloWorld。
    标示当前名为HelloWorld

    父类索引

    也是2个字节
    screenshot
    指向常量中第三个常量,对应内容为java/lang/Object

    接口数量

    screenshot
    表示接口数量为0
    screenshot

    字段表集合

    00 01 标示字段数量为1
    字段表的格式如下

    类型名称数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_coun

    accessFlags为 00 00 当时当前字段无修饰符
    字段修饰符格式如下

    标志名称标志值含义
    ACC_PUBLIC 0x0001 字段是否为public
    ACC_PRIVATE 0x0002 字段是否为private
    ACC_PROTECTED 0x0004 字段是否为protected
    ACC_STATIC 0x0008 字段是否为static
    ACC_FINAL 0x0010 字段是否为final
    ACC_VOLATILE 0x0040 字段是否为volatile
    ACC_TRANSIENT 0x0080 字段是否为transient
    ACC_SYNTHETIC 0x1000 字段是否为编译器自动产生
    ACC_ENUM 0x4000 字段是否为enum

    name_index为 00 05指向常量池中的第五个常量
    第5个常量为str,变量名为str
    screenshot
    descriptor_index指向常量池第6个变量,为Ljava/lang/String类型
    attributes_count(属性计数器,占2字节,0x0000,所以该字段没有额外需要描述的信息)

    方法集合

    screenshot
    method_count: 00 03 有3个方法
    methods:方法表集合

    类型名称数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_coun

    access flags的定义见下表

    标志名称标志值含义
    ACC_PUBLIC 0x0001 字段是否为public
    ACC_PRIVATE 0x0002 字段是否为private
    ACC_PROTECTED 0x0004 字段是否为protected
    ACC_STATIC 0x0008 字段是否为static
    ACC_FINAL 0x0010 字段是否为final
    ACC_SYNCHRONIZED 0x0020 字段是否为synchronized
    ACC_BRIDGE 0x0040 方法是否是由编译器产生的桥接方法
    ACC_VARARGS 0x0080 方法是否接受不定参数
    ACC_NATIVE 0x0100 字段是否为native
    ACC_ABSTRACT 0x0400 字段是否为abstract
    ACC_STRICTFP 0x0800 字段是否为strictfp
    ACC_SYNTHETIC 0x1000 字段是否为编译器自动产生

    这里方法access flags 为 00 01 说明方法为public的
    name_index为00 07,方法名指向常量中第7个常量方法名为, descriptor_index为常量池第8个常量()V
    screenshot
    screenshot
    attributes_count 为 00 01标示这个方法的属性表集合中有一个属性。属性名称为接下来2位0x0009,指向常量池中第9个常量:Code
    接下来4位为 00 00 00 3D标示Code属性值的字节长度为3D,接下来为00 02标示该方法的操作数栈的深度最大值为2.
    00 01标示该方法的局部变量占用空间为1.
    screenshot
    接下来4位00 00 00 0B 为机器编译生成字节码指令的长度为11,后面11个字节就是字节码指令(字节码指令可查询虚拟机字节码指令表),这里字节码指令长度用4个字节标示,所有字节码指令超长Class编译会失败的。
    再接下来为 00 00标示Code属性异常表结合为空。
    再后面为 00 02,,说明Code带有2个属性, 00 10即为Code属性第一个属性的属性名成指向常量池中第16个常量
    接下来的00 00 00 0E 标示LinueNumberTable属性值所占字节长度为15.接下来2位 00 03标示该line number table中有3个line number table表,start pc为 00 00 line number第 00 01个为00 04 第 00 02个为 00 0A 
    再后面的 00 01又是第二个方法的access flags,接着开始第二个方法。

    from: https://yq.aliyun.com/articles/7241?spm=5176.100239.blogcont37179.10.F6pHuW

     
    0
  • 相关阅读:
    用Springboot写一个只有一个条件的复杂查询
    Springboot The requested profile "pom.xml" could not be activated because it doesn't not exists
    springboot配置路径
    vuex
    @MappedSuperclass的作用
    icon.css
    default.html
    WebService调用
    通用分页存储过程
    存储过程获得最新订单号
  • 原文地址:https://www.cnblogs.com/KingIceMou/p/6967525.html
Copyright © 2011-2022 走看看